feat: initial commit

This commit is contained in:
2026-04-10 16:50:03 +07:00
parent 071b1f1515
commit 1e8d6a9b19
157 changed files with 29900 additions and 122 deletions

View File

@@ -0,0 +1,292 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Eval Set Review - __SKILL_NAME_PLACEHOLDER__</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600&family=Lora:wght@400;500&display=swap"
rel="stylesheet"
/>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Lora', Georgia, serif;
background: #faf9f5;
padding: 2rem;
color: #141413;
}
h1 {
font-family: 'Poppins', sans-serif;
margin-bottom: 0.5rem;
font-size: 1.5rem;
}
.description {
color: #b0aea5;
margin-bottom: 1.5rem;
font-style: italic;
max-width: 900px;
}
.controls {
margin-bottom: 1rem;
display: flex;
gap: 0.5rem;
}
.btn {
font-family: 'Poppins', sans-serif;
padding: 0.5rem 1rem;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
}
.btn-add {
background: #6a9bcc;
color: white;
}
.btn-add:hover {
background: #5889b8;
}
.btn-export {
background: #d97757;
color: white;
}
.btn-export:hover {
background: #c4613f;
}
table {
width: 100%;
max-width: 1100px;
border-collapse: collapse;
background: white;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
th {
font-family: 'Poppins', sans-serif;
background: #141413;
color: #faf9f5;
padding: 0.75rem 1rem;
text-align: left;
font-size: 0.875rem;
}
td {
padding: 0.75rem 1rem;
border-bottom: 1px solid #e8e6dc;
vertical-align: top;
}
tr:nth-child(even) td {
background: #faf9f5;
}
tr:hover td {
background: #f3f1ea;
}
.section-header td {
background: #e8e6dc;
font-family: 'Poppins', sans-serif;
font-weight: 500;
font-size: 0.8rem;
color: #141413;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.query-input {
width: 100%;
padding: 0.4rem;
border: 1px solid #e8e6dc;
border-radius: 4px;
font-size: 0.875rem;
font-family: 'Lora', Georgia, serif;
resize: vertical;
min-height: 60px;
}
.query-input:focus {
outline: none;
border-color: #d97757;
box-shadow: 0 0 0 2px rgba(217, 119, 87, 0.15);
}
.toggle {
position: relative;
display: inline-block;
width: 44px;
height: 24px;
}
.toggle input {
opacity: 0;
width: 0;
height: 0;
}
.toggle .slider {
position: absolute;
inset: 0;
background: #b0aea5;
border-radius: 24px;
cursor: pointer;
transition: 0.2s;
}
.toggle .slider::before {
content: '';
position: absolute;
width: 18px;
height: 18px;
left: 3px;
bottom: 3px;
background: white;
border-radius: 50%;
transition: 0.2s;
}
.toggle input:checked + .slider {
background: #d97757;
}
.toggle input:checked + .slider::before {
transform: translateX(20px);
}
.btn-delete {
background: #c44;
color: white;
padding: 0.3rem 0.6rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.75rem;
font-family: 'Poppins', sans-serif;
}
.btn-delete:hover {
background: #a33;
}
.summary {
margin-top: 1rem;
color: #b0aea5;
font-size: 0.875rem;
}
</style>
</head>
<body>
<h1>Eval Set Review: <span id="skill-name">__SKILL_NAME_PLACEHOLDER__</span></h1>
<p class="description">
Current description: <span id="skill-desc">__SKILL_DESCRIPTION_PLACEHOLDER__</span>
</p>
<div class="controls">
<button class="btn btn-add" onclick="addRow()">+ Add Query</button>
<button class="btn btn-export" onclick="exportEvalSet()">Export Eval Set</button>
</div>
<table>
<thead>
<tr>
<th style="width: 65%">Query</th>
<th style="width: 18%">Should Trigger</th>
<th style="width: 10%">Actions</th>
</tr>
</thead>
<tbody id="eval-body"></tbody>
</table>
<p class="summary" id="summary"></p>
<script>
const EVAL_DATA = __EVAL_DATA_PLACEHOLDER__;
let evalItems = [...EVAL_DATA];
function render() {
const tbody = document.getElementById('eval-body');
tbody.innerHTML = '';
// Sort: should-trigger first, then should-not-trigger
const sorted = evalItems
.map((item, origIdx) => ({ ...item, origIdx }))
.sort((a, b) => (b.should_trigger ? 1 : 0) - (a.should_trigger ? 1 : 0));
let lastGroup = null;
sorted.forEach((item) => {
const group = item.should_trigger ? 'trigger' : 'no-trigger';
if (group !== lastGroup) {
const headerRow = document.createElement('tr');
headerRow.className = 'section-header';
headerRow.innerHTML = `<td colspan="3">${item.should_trigger ? 'Should Trigger' : 'Should NOT Trigger'}</td>`;
tbody.appendChild(headerRow);
lastGroup = group;
}
const idx = item.origIdx;
const tr = document.createElement('tr');
tr.innerHTML = `
<td><textarea class="query-input" onchange="updateQuery(${idx}, this.value)">${escapeHtml(item.query)}</textarea></td>
<td>
<label class="toggle">
<input type="checkbox" ${item.should_trigger ? 'checked' : ''} onchange="updateTrigger(${idx}, this.checked)">
<span class="slider"></span>
</label>
<span style="margin-left:8px;font-size:0.8rem;color:#b0aea5">${item.should_trigger ? 'Yes' : 'No'}</span>
</td>
<td><button class="btn-delete" onclick="deleteRow(${idx})">Delete</button></td>
`;
tbody.appendChild(tr);
});
updateSummary();
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function updateQuery(idx, value) {
evalItems[idx].query = value;
updateSummary();
}
function updateTrigger(idx, value) {
evalItems[idx].should_trigger = value;
render();
}
function deleteRow(idx) {
evalItems.splice(idx, 1);
render();
}
function addRow() {
evalItems.push({ query: '', should_trigger: true });
render();
const inputs = document.querySelectorAll('.query-input');
inputs[inputs.length - 1].focus();
}
function updateSummary() {
const trigger = evalItems.filter((i) => i.should_trigger).length;
const noTrigger = evalItems.filter((i) => !i.should_trigger).length;
document.getElementById('summary').textContent =
`${evalItems.length} queries total: ${trigger} should trigger, ${noTrigger} should not trigger`;
}
function exportEvalSet() {
const valid = evalItems.filter((i) => i.query.trim() !== '');
const data = valid.map((i) => ({
query: i.query.trim(),
should_trigger: i.should_trigger
}));
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'eval_set.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
render();
</script>
</body>
</html>