completion-kit 0.1.0.rc1

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 (123) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +192 -0
  4. data/Rakefile +12 -0
  5. data/app/assets/config/completion_kit_manifest.js +1 -0
  6. data/app/assets/config/manifest.js +3 -0
  7. data/app/assets/images/completion_kit/logo.svg +6 -0
  8. data/app/assets/javascripts/completion_kit/evaluation_steps_controller.js +25 -0
  9. data/app/assets/stylesheets/completion_kit/application.css +2214 -0
  10. data/app/controllers/completion_kit/api/v1/base_controller.rb +29 -0
  11. data/app/controllers/completion_kit/api/v1/criteria_controller.rb +62 -0
  12. data/app/controllers/completion_kit/api/v1/datasets_controller.rb +51 -0
  13. data/app/controllers/completion_kit/api/v1/metrics_controller.rb +51 -0
  14. data/app/controllers/completion_kit/api/v1/prompts_controller.rb +64 -0
  15. data/app/controllers/completion_kit/api/v1/provider_credentials_controller.rb +51 -0
  16. data/app/controllers/completion_kit/api/v1/responses_controller.rb +32 -0
  17. data/app/controllers/completion_kit/api/v1/runs_controller.rb +71 -0
  18. data/app/controllers/completion_kit/api_reference_controller.rb +9 -0
  19. data/app/controllers/completion_kit/application_controller.rb +31 -0
  20. data/app/controllers/completion_kit/criteria_controller.rb +67 -0
  21. data/app/controllers/completion_kit/datasets_controller.rb +53 -0
  22. data/app/controllers/completion_kit/mcp_controller.rb +57 -0
  23. data/app/controllers/completion_kit/metrics_controller.rb +52 -0
  24. data/app/controllers/completion_kit/prompts_controller.rb +69 -0
  25. data/app/controllers/completion_kit/provider_credentials_controller.rb +63 -0
  26. data/app/controllers/completion_kit/responses_controller.rb +44 -0
  27. data/app/controllers/completion_kit/runs_controller.rb +131 -0
  28. data/app/helpers/completion_kit/application_helper.rb +193 -0
  29. data/app/jobs/completion_kit/application_job.rb +4 -0
  30. data/app/jobs/completion_kit/generate_job.rb +12 -0
  31. data/app/jobs/completion_kit/judge_job.rb +12 -0
  32. data/app/jobs/completion_kit/model_discovery_job.rb +29 -0
  33. data/app/mailers/completion_kit/application_mailer.rb +6 -0
  34. data/app/models/completion_kit/application_record.rb +5 -0
  35. data/app/models/completion_kit/criteria.rb +22 -0
  36. data/app/models/completion_kit/criteria_membership.rb +20 -0
  37. data/app/models/completion_kit/dataset.rb +24 -0
  38. data/app/models/completion_kit/metric.rb +97 -0
  39. data/app/models/completion_kit/model.rb +13 -0
  40. data/app/models/completion_kit/prompt.rb +99 -0
  41. data/app/models/completion_kit/provider_credential.rb +114 -0
  42. data/app/models/completion_kit/response.rb +30 -0
  43. data/app/models/completion_kit/review.rb +28 -0
  44. data/app/models/completion_kit/run.rb +253 -0
  45. data/app/models/completion_kit/run_metric.rb +6 -0
  46. data/app/models/completion_kit/suggestion.rb +8 -0
  47. data/app/services/completion_kit/anthropic_client.rb +86 -0
  48. data/app/services/completion_kit/api_config.rb +80 -0
  49. data/app/services/completion_kit/csv_processor.rb +65 -0
  50. data/app/services/completion_kit/judge_service.rb +87 -0
  51. data/app/services/completion_kit/llm_client.rb +45 -0
  52. data/app/services/completion_kit/mcp_dispatcher.rb +53 -0
  53. data/app/services/completion_kit/mcp_tools/criteria.rb +106 -0
  54. data/app/services/completion_kit/mcp_tools/datasets.rb +90 -0
  55. data/app/services/completion_kit/mcp_tools/metrics.rb +98 -0
  56. data/app/services/completion_kit/mcp_tools/prompts.rb +112 -0
  57. data/app/services/completion_kit/mcp_tools/provider_credentials.rb +97 -0
  58. data/app/services/completion_kit/mcp_tools/responses.rb +45 -0
  59. data/app/services/completion_kit/mcp_tools/runs.rb +130 -0
  60. data/app/services/completion_kit/model_discovery_service.rb +223 -0
  61. data/app/services/completion_kit/ollama_client.rb +80 -0
  62. data/app/services/completion_kit/open_ai_client.rb +71 -0
  63. data/app/services/completion_kit/open_router_client.rb +69 -0
  64. data/app/services/completion_kit/prompt_improvement_service.rb +81 -0
  65. data/app/views/completion_kit/api_reference/_example.html.erb +6 -0
  66. data/app/views/completion_kit/api_reference/index.html.erb +308 -0
  67. data/app/views/completion_kit/criteria/_form.html.erb +46 -0
  68. data/app/views/completion_kit/criteria/edit.html.erb +14 -0
  69. data/app/views/completion_kit/criteria/index.html.erb +37 -0
  70. data/app/views/completion_kit/criteria/new.html.erb +13 -0
  71. data/app/views/completion_kit/criteria/show.html.erb +37 -0
  72. data/app/views/completion_kit/datasets/_form.html.erb +29 -0
  73. data/app/views/completion_kit/datasets/edit.html.erb +13 -0
  74. data/app/views/completion_kit/datasets/index.html.erb +38 -0
  75. data/app/views/completion_kit/datasets/new.html.erb +12 -0
  76. data/app/views/completion_kit/datasets/show.html.erb +45 -0
  77. data/app/views/completion_kit/metrics/_form.html.erb +72 -0
  78. data/app/views/completion_kit/metrics/edit.html.erb +13 -0
  79. data/app/views/completion_kit/metrics/index.html.erb +34 -0
  80. data/app/views/completion_kit/metrics/new.html.erb +12 -0
  81. data/app/views/completion_kit/metrics/show.html.erb +49 -0
  82. data/app/views/completion_kit/prompts/_form.html.erb +52 -0
  83. data/app/views/completion_kit/prompts/edit.html.erb +13 -0
  84. data/app/views/completion_kit/prompts/index.html.erb +46 -0
  85. data/app/views/completion_kit/prompts/new.html.erb +12 -0
  86. data/app/views/completion_kit/prompts/show.html.erb +156 -0
  87. data/app/views/completion_kit/provider_credentials/_discovery_status.html.erb +30 -0
  88. data/app/views/completion_kit/provider_credentials/_form.html.erb +71 -0
  89. data/app/views/completion_kit/provider_credentials/edit.html.erb +12 -0
  90. data/app/views/completion_kit/provider_credentials/index.html.erb +41 -0
  91. data/app/views/completion_kit/provider_credentials/new.html.erb +12 -0
  92. data/app/views/completion_kit/responses/show.html.erb +87 -0
  93. data/app/views/completion_kit/runs/_actions.html.erb +14 -0
  94. data/app/views/completion_kit/runs/_form.html.erb +159 -0
  95. data/app/views/completion_kit/runs/_progress.html.erb +18 -0
  96. data/app/views/completion_kit/runs/_response_row.html.erb +13 -0
  97. data/app/views/completion_kit/runs/_sort_toolbar.html.erb +8 -0
  98. data/app/views/completion_kit/runs/_status_header.html.erb +15 -0
  99. data/app/views/completion_kit/runs/edit.html.erb +14 -0
  100. data/app/views/completion_kit/runs/index.html.erb +43 -0
  101. data/app/views/completion_kit/runs/new.html.erb +12 -0
  102. data/app/views/completion_kit/runs/show.html.erb +79 -0
  103. data/app/views/completion_kit/runs/suggestion.html.erb +47 -0
  104. data/app/views/layouts/completion_kit/application.html.erb +77 -0
  105. data/config/routes.rb +55 -0
  106. data/db/migrate/20260311000001_create_completion_kit_tables.rb +87 -0
  107. data/db/migrate/20260326000001_rename_criteria_to_instruction_on_metrics_and_reviews.rb +6 -0
  108. data/db/migrate/20260327000001_add_progress_to_runs.rb +6 -0
  109. data/db/migrate/20260327100001_replace_criteria_with_direct_metrics_on_runs.rb +12 -0
  110. data/db/migrate/20260328000001_add_error_message_to_runs.rb +5 -0
  111. data/db/migrate/20260329000001_create_completion_kit_models.rb +20 -0
  112. data/db/migrate/20260401170001_add_discovery_columns_to_completion_kit_provider_credentials.rb +7 -0
  113. data/db/migrate/20260403000001_add_temperature_to_completion_kit_runs.rb +5 -0
  114. data/db/migrate/20260403000002_create_completion_kit_suggestions.rb +13 -0
  115. data/db/migrate/20260403000003_add_applied_at_to_completion_kit_suggestions.rb +5 -0
  116. data/lib/completion-kit.rb +1 -0
  117. data/lib/completion_kit/engine.rb +35 -0
  118. data/lib/completion_kit/version.rb +3 -0
  119. data/lib/completion_kit.rb +55 -0
  120. data/lib/generators/completion_kit/install_generator.rb +21 -0
  121. data/lib/generators/completion_kit/templates/README +20 -0
  122. data/lib/generators/completion_kit/templates/initializer.rb +43 -0
  123. metadata +361 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cdecf15deb685a524186a2bd4ba48268e10475da0d2cc2914969318893268f70
4
+ data.tar.gz: ce395abf147434f9a825f79902e47b171b074038d20c06bf7b86b80ba70eaa00
5
+ SHA512:
6
+ metadata.gz: 9d13fd1d1863c87ca0f7ed78eb1853ac921ff39862b84c678e5b5c9977f1832edcfb50c52b2371f2dc88ad6360be22a78d39ef53385239939a1ac9297df444ff
7
+ data.tar.gz: bf95df5b178ccfe455350d216a894836a85cfeff5c117d0206b87211ef638c1d690790350168b5a9ad84e8e3895add8c89acdea98738a1c0d2025115679d939f
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,192 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/homemade-software-inc/completion-kit/main/docs/logo.png" alt="CompletionKit logo" width="120" />
3
+ </p>
4
+
5
+ # CompletionKit
6
+
7
+ [![CI](https://github.com/homemade-software-inc/completion-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/homemade-software-inc/completion-kit/actions/workflows/ci.yml)
8
+ ![coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
9
+ ![dependencies](https://img.shields.io/badge/dependencies-7-blue)
10
+ [![Dependabot](https://img.shields.io/badge/dependabot-enabled-blue?logo=dependabot)](https://github.com/homemade-software-inc/completion-kit/network/updates)
11
+
12
+ You need to know whether your prompts produce the output you expect, consistently, across real data. CompletionKit gives you that, inside your Rails app.
13
+
14
+ Mount the engine, bring your prompts and datasets, and every input runs through a model you pick. Each output is scored against your own metrics and rubrics by an LLM-as-judge. When you change a prompt, re-run the same dataset and see exactly what got better and what broke — and when the scores tell you something's off, CompletionKit can suggest an improved version of the prompt based on the reviews, which you inspect as a diff and apply as a new version.
15
+
16
+ Drive it from the web UI, from the REST API, or from Claude Code and other MCP-aware agents via the built-in Model Context Protocol server. All three share the same state — your prompts, runs, datasets, and scores are one source of truth.
17
+
18
+ It's the difference between "this prompt seems to work" and "this prompt scores 4.3 out of 5 across 200 inputs, up from 3.8 last version."
19
+
20
+ ![Prompts index](https://raw.githubusercontent.com/homemade-software-inc/completion-kit/main/docs/screenshots/prompts.png)
21
+
22
+ ![Prompt detail with metrics and rubrics](https://raw.githubusercontent.com/homemade-software-inc/completion-kit/main/docs/screenshots/prompt-detail.png)
23
+
24
+ ![Test run with scored results](https://raw.githubusercontent.com/homemade-software-inc/completion-kit/main/docs/screenshots/test-run.png)
25
+
26
+ ## Setup
27
+
28
+ ```ruby
29
+ gem "completion-kit"
30
+ ```
31
+
32
+ ```bash
33
+ bin/rails generate completion_kit:install
34
+ bin/rails db:migrate
35
+ ```
36
+
37
+ Set your provider keys via environment variables or the generated initializer:
38
+
39
+ ```bash
40
+ OPENAI_API_KEY=...
41
+ ANTHROPIC_API_KEY=...
42
+ LLAMA_API_KEY=...
43
+ LLAMA_API_ENDPOINT=...
44
+ ```
45
+
46
+ Available models are discovered dynamically from each provider's API.
47
+
48
+ ### Encryption keys
49
+
50
+ Provider API keys are stored using [Rails Active Record encryption](https://guides.rubyonrails.org/active_record_encryption.html), so the host app must have encryption keys configured. If you haven't set them up already:
51
+
52
+ ```bash
53
+ bin/rails db:encryption:init
54
+ ```
55
+
56
+ Copy the generated keys into `config/credentials.yml.enc` under `active_record_encryption`, or set the equivalent environment variables. CompletionKit won't boot without valid keys in production.
57
+
58
+ ## Authentication
59
+
60
+ CompletionKit requires authentication in production. In development, routes are open by default (with a log warning).
61
+
62
+ ### Basic Auth (recommended for simple setups)
63
+
64
+ ```ruby
65
+ # config/initializers/completion_kit.rb
66
+ CompletionKit.configure do |c|
67
+ c.username = "admin"
68
+ c.password = ENV["COMPLETION_KIT_PASSWORD"]
69
+ end
70
+ ```
71
+
72
+ ### Custom Auth (Devise, etc.)
73
+
74
+ ```ruby
75
+ # config/initializers/completion_kit.rb
76
+ CompletionKit.configure do |c|
77
+ c.auth_strategy = ->(controller) { controller.authenticate_user! }
78
+ end
79
+ ```
80
+
81
+ Only one mode can be active — setting both raises a `ConfigurationError`.
82
+
83
+ ## Usage
84
+
85
+ 1. Create a prompt with `{{variable}}` placeholders
86
+ 2. Create a test run and paste CSV data (headers match variable names)
87
+ 3. Generate outputs, run AI review, inspect scored results
88
+
89
+ ## Programmatic access
90
+
91
+ CompletionKit exposes every resource through both a REST JSON API and an MCP server. Both share the same bearer-token auth, so configure once and use either interface:
92
+
93
+ ```ruby
94
+ # config/initializers/completion_kit.rb
95
+ CompletionKit.configure { |c| c.api_token = ENV["COMPLETION_KIT_API_TOKEN"] }
96
+ ```
97
+
98
+ ### Concepts
99
+
100
+ These are the objects you'll work with, whether through the UI, the REST API, or the MCP server:
101
+
102
+ - **Prompt** — A named, versioned template with `{{variable}}` placeholders. Publishing a prompt freezes its template so runs always reference a known version; editing a published prompt creates a new version.
103
+ - **Dataset** — A CSV of real inputs. Column headers match the prompt's `{{variable}}` names, and each row becomes one test case.
104
+ - **Run** — A single execution of a prompt against a dataset. Tracks progress, stores outputs, and records which metrics were used for scoring.
105
+ - **Response** — The model's output for one row of the dataset, with any reviews attached.
106
+ - **Metric** — One evaluation dimension: a name, an instruction, evaluation steps, and 1–5-star rubric bands. The judge uses a metric to score a response.
107
+ - **Criteria** — A named, reusable bundle of metrics you can apply to a run in one step.
108
+ - **Provider Credential** — An API key for a model provider (OpenAI, Anthropic, Ollama, OpenRouter). Encrypted at rest using Rails' Active Record encryption, and never returned through the API.
109
+
110
+ ### REST API
111
+
112
+ ```bash
113
+ curl -H "Authorization: Bearer $TOKEN" \
114
+ http://localhost:3000/completion_kit/api/v1/prompts
115
+
116
+ curl -X POST http://localhost:3000/completion_kit/api/v1/prompts \
117
+ -H "Authorization: Bearer $TOKEN" \
118
+ -H "Content-Type: application/json" \
119
+ -d '{"name":"summarizer","template":"Summarize: {{text}}","llm_model":"gpt-4.1"}'
120
+ ```
121
+
122
+ Mount the engine, then visit **`/completion_kit/api_reference`** in your running app for per-endpoint documentation with copy-to-clipboard curl examples pre-filled with your token.
123
+
124
+ ### MCP server
125
+
126
+ CompletionKit also runs a [Model Context Protocol](https://modelcontextprotocol.io) server at the `/mcp` path within the engine mount, exposing the same resources as 36 tools (one per CRUD action plus process actions like `runs_generate` and `prompts_publish`). Point Claude Code, Cursor, or any other MCP client at it:
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "completion-kit": {
132
+ "url": "https://your-app.com/completion_kit/mcp",
133
+ "headers": { "Authorization": "Bearer YOUR_TOKEN" }
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ The in-app API reference page also ships install snippets you can copy straight into your MCP client config.
140
+
141
+ ## Standalone App
142
+
143
+ CompletionKit ships with a standalone Rails app you can deploy as a hosted service.
144
+
145
+ ### Quick Start
146
+
147
+ ```bash
148
+ cd standalone
149
+ bundle install
150
+ bin/rails completion_kit:install:migrations
151
+ bin/rails db:migrate
152
+ bin/rails server
153
+ ```
154
+
155
+ Visit `http://localhost:3000` for the home page, or `http://localhost:3000/completion_kit` for the engine UI.
156
+
157
+ ### Configuration
158
+
159
+ Set environment variables:
160
+
161
+ | Variable | Purpose | Default |
162
+ |----------|---------|---------|
163
+ | `COMPLETION_KIT_API_TOKEN` | Bearer token for REST API and MCP access | (none — API disabled) |
164
+ | `COMPLETION_KIT_USERNAME` | Web UI login username | `admin` |
165
+ | `COMPLETION_KIT_PASSWORD` | Web UI login password | (none — open in dev) |
166
+ | `DATABASE_URL` | PostgreSQL connection string (production) | SQLite in dev |
167
+
168
+ ### Deploying
169
+
170
+ Any Rails-friendly host works — Fly, Heroku, Render, self-managed Docker, etc. Point your host at a Postgres instance via `DATABASE_URL`, set the environment variables above, and run `cd standalone && bin/rails db:migrate` on each deploy.
171
+
172
+ When the gem ships a new engine migration, install it into your standalone app locally and commit the generated file before pushing:
173
+
174
+ ```bash
175
+ cd standalone
176
+ bin/rails completion_kit:install:migrations
177
+ bin/rails db:migrate
178
+ git add db/migrate/ && git commit -m "install new engine migration"
179
+ ```
180
+
181
+ That way your host's `db:migrate` picks up the new file on the next deploy. Don't run `completion_kit:install:migrations` on the host itself — migration files are source artifacts, they belong in git.
182
+
183
+ ## Development
184
+
185
+ ```bash
186
+ bundle install
187
+ bundle exec rspec
188
+ ```
189
+
190
+ ## License
191
+
192
+ [MIT](https://opensource.org/licenses/MIT)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/setup"
2
+
3
+ load "rails/tasks/statistics.rake"
4
+
5
+ require "bundler/gem_tasks"
6
+ require "rspec/core/rake_task"
7
+
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.rspec_opts = "--default-path spec"
10
+ end
11
+
12
+ task default: :spec
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/completion_kit .css
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../stylesheets .css
3
+ //= link_directory ../stylesheets/completion_kit .css
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="2.4 2.3 88.74 89.1" width="512" height="512">
2
+ <path d="M57.7,56.4c1.2-1.2,1.8-3.1,1.4-5c-0.5-2-2.1-3.6-4.1-4c-3.5-0.7-6.6,1.9-6.6,5.3c0,1.5,0.6,2.8,1.5,3.8H37.4l0-12.6c-1.2,1.2-3.1,1.8-5,1.4c-2-0.5-3.6-2.1-4-4.1c-0.7-3.5,1.9-6.6,5.3-6.6c1.5,0,2.8,0.6,3.8,1.5V23.5H10.1c-3.1,0-5.7,2.5-5.7,5.7v54.5c0,3.1,2.5,5.7,5.7,5.7h27.2h27.2c3.1,0,5.7-2.5,5.7-5.7V56.4H57.7z" fill="#0891B2"/>
3
+ <path d="M54.7,37.5c-0.1-1.7,0.6-3.5,2.2-4.8c1.6-1.2,3.9-1.4,5.7-0.5c3.2,1.7,3.8,5.7,1.6,8.3c-0.9,1.1-2.3,1.8-3.6,1.9l9.6,8.1l17.5-20.9c2-2.4,1.7-6-0.7-8L66.2,4.3l-8.1,9.6c-0.2-1.7-1.2-3.4-3-4.3c-1.8-0.9-4.1-0.7-5.7,0.5c-2.8,2.2-2.8,6.3-0.2,8.4c1.1,0.9,2.5,1.4,3.9,1.2L45,29.5L54.7,37.5z" fill="#06B6D4"/>
4
+ <path d="M70.2,52.6c-0.5,0-0.9-0.2-1.3-0.5L59.3,44c-0.6-0.5-0.9-1.4-0.6-2.1c0.2-0.8,0.9-1.3,1.7-1.4c0.9-0.1,1.7-0.5,2.3-1.2c0.7-0.8,0.9-1.8,0.7-2.8c-0.2-1-0.8-1.9-1.8-2.4c-1.1-0.6-2.6-0.5-3.6,0.3c-1,0.7-1.5,1.8-1.4,3c0.1,0.8-0.4,1.6-1.1,1.9c-0.7,0.4-1.6,0.3-2.2-0.2L43.8,31c-0.4-0.3-0.7-0.8-0.7-1.4c0-0.5,0.1-1.1,0.5-1.5l5.9-7.1c-0.5-0.3-1-0.6-1.5-1c-1.7-1.4-2.7-3.6-2.6-5.8c0-2.2,1.1-4.3,2.9-5.7c2.2-1.8,5.3-2.1,7.9-0.7c1.1,0.6,2,1.3,2.6,2.3l6-7.1C65,2.6,65.5,2.3,66,2.3c0.5,0,1.1,0.1,1.5,0.5l20.9,17.5c1.6,1.3,2.5,3.2,2.7,5.2s-0.4,4-1.8,5.6L71.8,51.9c-0.3,0.4-0.8,0.7-1.4,0.7C70.4,52.6,70.3,52.6,70.2,52.6z M64.4,43.1l5.6,4.7l16.2-19.3c0.6-0.8,0.9-1.7,0.8-2.7s-0.5-1.9-1.3-2.5L66.4,7.1l-6.8,8.1c-0.5,0.6-1.4,0.9-2.1,0.6c-0.8-0.2-1.3-0.9-1.4-1.7c-0.1-1.2-0.8-2.2-1.9-2.7c-1.1-0.6-2.6-0.4-3.6,0.3c-0.9,0.7-1.3,1.6-1.4,2.7c0,1,0.4,2,1.2,2.7c0.7,0.6,1.5,0.9,2.4,0.8c0.8-0.1,1.6,0.4,1.9,1.1s0.3,1.6-0.2,2.2l-6.8,8.1l5.6,4.7c0.5-1,1.3-2,2.2-2.7c2.3-1.7,5.4-2,7.9-0.6c2,1.1,3.4,3,3.8,5.2s-0.2,4.5-1.6,6.2C65.4,42.4,64.9,42.8,64.4,43.1z" fill="#475569"/>
5
+ <path d="M70.3,54.4H61c0.3-1.1,0.3-2.3,0-3.5c-0.6-2.8-2.8-5-5.6-5.5c-2.2-0.5-4.5,0.1-6.3,1.5c-1.7,1.4-2.7,3.5-2.7,5.8c0,0.6,0.1,1.2,0.2,1.8h-7.3V43.8c0-0.8-0.5-1.5-1.2-1.8c-0.7-0.3-1.6-0.1-2.2,0.4c-0.8,0.8-2,1.2-3.2,0.9c-1.2-0.3-2.2-1.3-2.5-2.5c-0.2-1.1,0-2.1,0.7-2.9c0.7-0.8,1.6-1.3,2.6-1.3c0.9,0,1.7,0.3,2.4,1c0.6,0.6,1.4,0.7,2.2,0.4c0.7-0.3,1.2-1,1.2-1.8V23.5c0-1.1-0.9-2-2-2H10.1c-4.2,0-7.7,3.4-7.7,7.7v54.5c0,4.2,3.4,7.7,7.7,7.7h54.5c4.2,0,7.7-3.4,7.7-7.7V56.4C72.3,55.3,71.4,54.4,70.3,54.4z M10.1,25.5h25.2v7.3c-0.6-0.1-1.2-0.2-1.8-0.2c-2.2,0-4.3,1-5.8,2.7s-2,4-1.5,6.3c0.6,2.8,2.8,5,5.5,5.6c1.2,0.3,2.4,0.2,3.5,0v7.3H24.8c-0.8,0-1.5,0.5-1.8,1.2c-0.3,0.7-0.1,1.6,0.4,2.2c0.8,0.8,1.1,2,0.9,3.2c-0.3,1.2-1.3,2.2-2.6,2.5c-1.1,0.2-2.1,0-2.9-0.7c-0.8-0.7-1.3-1.6-1.3-2.6c0-0.9,0.3-1.7,1-2.4c0.6-0.6,0.7-1.4,0.4-2.2c-0.3-0.7-1-1.2-1.8-1.2H6.5V29.2C6.5,27.1,8.1,25.5,10.1,25.5z M36.6,74.9c-0.7,0.3-1.2,1-1.2,1.8v10.6H10.1c-2,0-3.7-1.6-3.7-3.7V58.4h7.3c-0.1,0.6-0.2,1.2-0.2,1.8c0,2.2,1,4.3,2.7,5.8c1.7,1.4,4,2,6.3,1.5c2.8-0.6,5-2.8,5.6-5.5c0.3-1.2,0.2-2.4,0-3.5h7.3V69c0,0.8,0.5,1.5,1.2,1.8c0.7,0.3,1.6,0.1,2.2-0.4c0.8-0.8,2-1.1,3.2-0.9c1.2,0.3,2.2,1.3,2.5,2.6c0.2,1.1,0,2.1-0.7,2.9c-0.7,0.8-1.6,1.3-2.6,1.3c-0.9,0-1.7-0.3-2.4-1C38.2,74.7,37.3,74.6,36.6,74.9z M68.3,83.6c0,2-1.6,3.7-3.7,3.7H39.4V80c0.6,0.1,1.2,0.2,1.8,0.2c2.2,0,4.3-1,5.8-2.7c1.4-1.7,2-4,1.5-6.3c-0.6-2.8-2.8-5-5.5-5.6c-1.2-0.3-2.4-0.2-3.5,0v-7.3H50c0.8,0,1.5-0.5,1.8-1.2c0.3-0.7,0.1-1.6-0.4-2.2c-0.6-0.6-1-1.5-1-2.4c0-1,0.5-2,1.3-2.6c0.8-0.7,1.8-0.9,2.9-0.7c1.2,0.3,2.3,1.3,2.6,2.5c0.3,1.2-0.1,2.4-0.9,3.2c-0.6,0.6-0.7,1.4-0.4,2.2c0.3,0.7,1,1.2,1.8,1.2h10.6V83.6z" fill="#475569"/>
6
+ </svg>
@@ -0,0 +1,25 @@
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ document.addEventListener("click", function (event) {
3
+ var addBtn = event.target.closest("[data-action='evaluation-steps#add']");
4
+ if (addBtn) {
5
+ var container = addBtn.closest("[data-controller='evaluation-steps']");
6
+ var list = container.querySelector("[data-evaluation-steps-target='list']");
7
+ var row = document.createElement("div");
8
+ row.className = "ck-step-row";
9
+ row.setAttribute("data-evaluation-steps-target", "row");
10
+ row.innerHTML =
11
+ '<input type="text" name="metric[evaluation_steps][]" value="" class="ck-input" placeholder="Describe this evaluation step..." />' +
12
+ '<button type="button" class="ck-icon-btn" data-action="evaluation-steps#remove" aria-label="Remove step">' +
13
+ '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>' +
14
+ "</button>";
15
+ list.appendChild(row);
16
+ row.querySelector("input").focus();
17
+ }
18
+
19
+ var removeBtn = event.target.closest("[data-action='evaluation-steps#remove']");
20
+ if (removeBtn) {
21
+ var stepRow = removeBtn.closest("[data-evaluation-steps-target='row']");
22
+ if (stepRow) stepRow.remove();
23
+ }
24
+ });
25
+ });