@hacksmith/doraval 0.2.48 → 0.2.50
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.
- package/README.md +2 -2
- package/bin/doraval.js +299 -287
- package/bin/ui/index.html +199 -56
- package/package.json +1 -1
package/bin/ui/index.html
CHANGED
|
@@ -118,40 +118,140 @@
|
|
|
118
118
|
.accent {
|
|
119
119
|
color: #60a5fa;
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
/* Sidebar navigation */
|
|
123
|
+
.sidebar {
|
|
124
|
+
position: fixed;
|
|
125
|
+
top: 0;
|
|
126
|
+
left: 0;
|
|
127
|
+
height: 100vh;
|
|
128
|
+
width: 200px;
|
|
129
|
+
background: #0a0a0a;
|
|
130
|
+
border-right: 1px solid #18181b;
|
|
131
|
+
display: flex;
|
|
132
|
+
flex-direction: column;
|
|
133
|
+
z-index: 40;
|
|
134
|
+
padding: 0;
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.nav-item {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: 10px;
|
|
142
|
+
padding: 9px 16px;
|
|
143
|
+
font-size: 13px;
|
|
144
|
+
color: #71717a;
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
border-left: 2px solid transparent;
|
|
147
|
+
transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
|
|
148
|
+
white-space: nowrap;
|
|
149
|
+
background: transparent;
|
|
150
|
+
border-top: none;
|
|
151
|
+
border-right: none;
|
|
152
|
+
border-bottom: none;
|
|
153
|
+
width: 100%;
|
|
154
|
+
text-align: left;
|
|
155
|
+
}
|
|
156
|
+
.nav-item:hover {
|
|
157
|
+
color: #d4d4d8;
|
|
158
|
+
background: #18181b;
|
|
159
|
+
}
|
|
160
|
+
.nav-item.active {
|
|
161
|
+
color: #e4e4e7;
|
|
162
|
+
background: #18181b;
|
|
163
|
+
border-left-color: #60a5fa;
|
|
164
|
+
}
|
|
165
|
+
.nav-icon {
|
|
166
|
+
font-size: 14px;
|
|
167
|
+
width: 18px;
|
|
168
|
+
text-align: center;
|
|
169
|
+
flex-shrink: 0;
|
|
170
|
+
}
|
|
171
|
+
.nav-section-label {
|
|
172
|
+
font-size: 10px;
|
|
173
|
+
font-weight: 600;
|
|
174
|
+
letter-spacing: 0.08em;
|
|
175
|
+
color: #3f3f46;
|
|
176
|
+
padding: 12px 16px 4px;
|
|
177
|
+
text-transform: uppercase;
|
|
178
|
+
}
|
|
179
|
+
.nav-divider {
|
|
180
|
+
height: 1px;
|
|
181
|
+
background: #18181b;
|
|
182
|
+
margin: 6px 0;
|
|
183
|
+
}
|
|
121
184
|
</style>
|
|
122
185
|
</head>
|
|
123
|
-
<body class="bg-zinc-950 text-zinc-200">
|
|
124
|
-
|
|
186
|
+
<body class="bg-zinc-950 text-zinc-200 flex">
|
|
187
|
+
<!-- Sidebar -->
|
|
188
|
+
<nav class="sidebar">
|
|
189
|
+
<!-- Brand -->
|
|
190
|
+
<div class="flex items-center gap-3 px-4 py-4 border-b border-zinc-900">
|
|
191
|
+
<div class="text-lg">🌀</div>
|
|
192
|
+
<div>
|
|
193
|
+
<div class="text-sm font-semibold tracking-tight text-zinc-200">dora</div>
|
|
194
|
+
<div id="sidebar-project" class="text-[10px] text-zinc-500 font-mono truncate max-w-[130px]"></div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<!-- Nav -->
|
|
199
|
+
<div class="flex-1 overflow-y-auto py-2">
|
|
200
|
+
<div class="nav-section-label">Views</div>
|
|
201
|
+
<button class="nav-item active" id="nav-capture" onclick="scrollToSection('section-capture')">
|
|
202
|
+
<span class="nav-icon">✏️</span>Capture
|
|
203
|
+
</button>
|
|
204
|
+
<button class="nav-item" id="nav-journal" onclick="scrollToSection('section-journal')">
|
|
205
|
+
<span class="nav-icon">🗒️</span>Journal
|
|
206
|
+
</button>
|
|
207
|
+
<button class="nav-item" id="nav-context" onclick="scrollToSection('section-context')">
|
|
208
|
+
<span class="nav-icon">👁️</span>Context
|
|
209
|
+
</button>
|
|
210
|
+
<button class="nav-item" id="nav-hooks" onclick="scrollToSection('section-hooks')">
|
|
211
|
+
<span class="nav-icon">🔗</span>Hooks
|
|
212
|
+
</button>
|
|
213
|
+
<button class="nav-item" id="nav-evals" onclick="scrollToSection('section-evals')">
|
|
214
|
+
<span class="nav-icon">📊</span>Evals
|
|
215
|
+
</button>
|
|
216
|
+
|
|
217
|
+
<div class="nav-divider"></div>
|
|
218
|
+
<div class="nav-section-label">Actions</div>
|
|
219
|
+
<button class="nav-item" onclick="doAction('sync')">
|
|
220
|
+
<span class="nav-icon">↑</span>Sync to remote
|
|
221
|
+
</button>
|
|
222
|
+
<button class="nav-item" onclick="doAction('update')">
|
|
223
|
+
<span class="nav-icon">↓</span>Pull latest
|
|
224
|
+
</button>
|
|
225
|
+
<button class="nav-item" onclick="doAction('open-dir')">
|
|
226
|
+
<span class="nav-icon">📁</span>Open data dir
|
|
227
|
+
</button>
|
|
228
|
+
<button class="nav-item" onclick="refreshAll()">
|
|
229
|
+
<span class="nav-icon">↺</span>Refresh
|
|
230
|
+
</button>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!-- Footer link -->
|
|
234
|
+
<div class="border-t border-zinc-900 px-4 py-3">
|
|
235
|
+
<a href="https://github.com/saif-shines/doraval" target="_blank"
|
|
236
|
+
class="text-[11px] text-zinc-600 hover:text-zinc-400 transition">
|
|
237
|
+
GitHub ↗
|
|
238
|
+
</a>
|
|
239
|
+
</div>
|
|
240
|
+
</nav>
|
|
241
|
+
|
|
242
|
+
<div class="flex-1 min-w-0 ml-[200px] p-8 max-w-[calc(100vw-200px)]">
|
|
125
243
|
<!-- Header -->
|
|
126
244
|
<div class="flex items-center justify-between mb-8">
|
|
127
|
-
<div
|
|
128
|
-
<div class="
|
|
129
|
-
|
|
130
|
-
</div>
|
|
131
|
-
<div>
|
|
132
|
-
<div class="font-semibold tracking-tighter text-2xl">dora</div>
|
|
133
|
-
<div class="text-[10px] text-zinc-500 -mt-1 tracking-[1px] font-mono">LOCAL DASHBOARD</div>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
|
|
137
|
-
<div class="flex items-center gap-2 text-sm">
|
|
138
|
-
<div id="project-badge" class="px-3.5 py-1 rounded-full bg-zinc-900 border border-zinc-800 text-xs font-medium"></div>
|
|
139
|
-
|
|
140
|
-
<button onclick="refreshAll()"
|
|
141
|
-
class="px-4 py-1.5 rounded-2xl bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 text-xs font-medium active:scale-[0.97] transition flex items-center gap-1.5">
|
|
142
|
-
<span>Refresh</span>
|
|
143
|
-
</button>
|
|
144
|
-
|
|
145
|
-
<button onclick="window.open('https://github.com/saif-shines/doraval','_blank')"
|
|
146
|
-
class="px-4 py-1.5 rounded-2xl bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 text-xs font-medium active:scale-[0.97] transition">
|
|
147
|
-
GitHub
|
|
148
|
-
</button>
|
|
245
|
+
<div>
|
|
246
|
+
<div class="font-semibold tracking-tighter text-xl text-zinc-200">Local dashboard</div>
|
|
247
|
+
<div class="text-[10px] text-zinc-500 mt-0.5 font-mono">quick capture & review — CLI for heavy lifting</div>
|
|
149
248
|
</div>
|
|
249
|
+
<div id="project-badge" class="px-3 py-1 rounded-full bg-zinc-900 border border-zinc-800 text-xs font-medium"></div>
|
|
150
250
|
</div>
|
|
151
251
|
|
|
152
252
|
<div class="grid grid-cols-1 lg:grid-cols-5 gap-5">
|
|
153
253
|
<!-- Capture -->
|
|
154
|
-
<div class="lg:col-span-2 section">
|
|
254
|
+
<div id="section-capture" class="lg:col-span-2 section">
|
|
155
255
|
<div class="flex items-baseline justify-between mb-4">
|
|
156
256
|
<div>
|
|
157
257
|
<div class="text-sm font-semibold tracking-tight">Capture decision</div>
|
|
@@ -206,7 +306,7 @@
|
|
|
206
306
|
</div>
|
|
207
307
|
|
|
208
308
|
<!-- What agents see -->
|
|
209
|
-
<div class="lg:col-span-3 section">
|
|
309
|
+
<div id="section-context" class="lg:col-span-3 section">
|
|
210
310
|
<div class="flex items-center justify-between mb-3">
|
|
211
311
|
<div class="flex items-center gap-2">
|
|
212
312
|
<div class="text-sm font-semibold tracking-tight">What agents will see</div>
|
|
@@ -221,21 +321,23 @@
|
|
|
221
321
|
<button onclick="copyContext()"
|
|
222
322
|
class="text-xs px-3 py-1 rounded-xl border border-zinc-800 hover:bg-zinc-900 active:scale-[0.97] transition">Copy</button>
|
|
223
323
|
</div>
|
|
224
|
-
<pre id="context" class="context bg-zinc-950 border border-zinc-800 rounded-2xl p-5 text-zinc-300 text-[13px] overflow-auto max-h-[220px] leading-relaxed"></pre>
|
|
324
|
+
<pre id="context" class="context bg-zinc-950 border border-zinc-800 rounded-2xl p-5 text-zinc-300 text-[13px] overflow-auto max-h-[220px] leading-relaxed"><span class="text-zinc-600 animate-pulse">Loading context...</span></pre>
|
|
225
325
|
<div id="agent-note" class="mt-2 text-[10px] text-zinc-500 px-1">Exact output from <span class="font-mono">dora journal context</span>. Injected via startup hook for the selected agent.</div>
|
|
226
326
|
</div>
|
|
227
327
|
|
|
228
328
|
<!-- Hooks -->
|
|
229
|
-
<div class="lg:col-span-2 section">
|
|
329
|
+
<div id="section-hooks" class="lg:col-span-2 section">
|
|
230
330
|
<div class="text-sm font-semibold tracking-tight mb-3">Journal hooks</div>
|
|
231
|
-
<div id="hooks" class="space-y-2 text-sm"
|
|
331
|
+
<div id="hooks" class="space-y-2 text-sm">
|
|
332
|
+
<div class="text-zinc-600 text-xs py-2 animate-pulse">Loading...</div>
|
|
333
|
+
</div>
|
|
232
334
|
<div class="mt-4 text-[10px] text-zinc-500 leading-snug">
|
|
233
335
|
Same as <span class="font-mono text-zinc-400">dora journal hook</span>. Restart Claude after toggling.
|
|
234
336
|
</div>
|
|
235
337
|
</div>
|
|
236
338
|
|
|
237
339
|
<!-- Journal -->
|
|
238
|
-
<div class="lg:col-span-3 section">
|
|
340
|
+
<div id="section-journal" class="lg:col-span-3 section">
|
|
239
341
|
<div class="flex items-center justify-between mb-4">
|
|
240
342
|
<div class="text-sm font-semibold tracking-tight">Journal entries</div>
|
|
241
343
|
<div class="flex items-center gap-2">
|
|
@@ -245,12 +347,14 @@
|
|
|
245
347
|
<div class="text-[10px] text-zinc-500 tabular-nums" id="entry-count"></div>
|
|
246
348
|
</div>
|
|
247
349
|
</div>
|
|
248
|
-
<div id="entries" class="space-y-2 text-sm max-h-[340px] overflow-auto pr-2 -mr-1"
|
|
350
|
+
<div id="entries" class="space-y-2 text-sm max-h-[340px] overflow-auto pr-2 -mr-1">
|
|
351
|
+
<div class="text-zinc-600 text-xs py-3 animate-pulse">Loading...</div>
|
|
352
|
+
</div>
|
|
249
353
|
</div>
|
|
250
354
|
</div>
|
|
251
355
|
|
|
252
356
|
<!-- Evals / Skill Learnings -->
|
|
253
|
-
<div class="lg:col-span-5 section">
|
|
357
|
+
<div id="section-evals" class="lg:col-span-5 section">
|
|
254
358
|
<div class="flex items-center justify-between mb-3">
|
|
255
359
|
<div>
|
|
256
360
|
<div class="text-sm font-semibold tracking-tight">Skill adherence learnings</div>
|
|
@@ -265,28 +369,12 @@
|
|
|
265
369
|
class="px-3 py-1 text-xs rounded-xl border border-zinc-800 hover:bg-zinc-900 active:scale-[0.97] transition">Refresh</button>
|
|
266
370
|
</div>
|
|
267
371
|
</div>
|
|
268
|
-
<div id="evals" class="space-y-1.5 text-sm max-h-[260px] overflow-auto pr-1 -mr-1"
|
|
372
|
+
<div id="evals" class="space-y-1.5 text-sm max-h-[260px] overflow-auto pr-1 -mr-1">
|
|
373
|
+
<div class="text-zinc-600 text-xs py-2 animate-pulse">Loading...</div>
|
|
374
|
+
</div>
|
|
269
375
|
<div class="mt-2 text-[10px] text-zinc-500">Click a row for full checklist. Run <span class="font-mono">dora eval</span> from the terminal to generate more.</div>
|
|
270
376
|
</div>
|
|
271
377
|
|
|
272
|
-
<!-- Footer actions -->
|
|
273
|
-
<div class="mt-8 flex items-center gap-2 text-sm">
|
|
274
|
-
<button onclick="doAction('sync')"
|
|
275
|
-
class="px-4 py-2 rounded-2xl border border-zinc-800 hover:bg-zinc-900 active:scale-[0.975] transition text-sm">
|
|
276
|
-
Sync to remote
|
|
277
|
-
</button>
|
|
278
|
-
<button onclick="doAction('update')"
|
|
279
|
-
class="px-4 py-2 rounded-2xl border border-zinc-800 hover:bg-zinc-900 active:scale-[0.975] transition text-sm">
|
|
280
|
-
Pull latest
|
|
281
|
-
</button>
|
|
282
|
-
<button onclick="doAction('open-dir')"
|
|
283
|
-
class="px-4 py-2 rounded-2xl border border-zinc-800 hover:bg-zinc-900 active:scale-[0.975] transition text-sm">
|
|
284
|
-
Open data dir
|
|
285
|
-
</button>
|
|
286
|
-
|
|
287
|
-
<div class="flex-1"></div>
|
|
288
|
-
<div class="text-xs text-zinc-500">CLI is best for heavy lifting. This is for quick capture & review. Data location depends on DORAVAL_HOME.</div>
|
|
289
|
-
</div>
|
|
290
378
|
</div>
|
|
291
379
|
|
|
292
380
|
<!-- Modal -->
|
|
@@ -362,6 +450,8 @@ async function loadStatus() {
|
|
|
362
450
|
const s = await fetchJSON('/api/status');
|
|
363
451
|
currentProject = s.project;
|
|
364
452
|
setProjectBadge(currentProject);
|
|
453
|
+
const sidebarProject = document.getElementById('sidebar-project');
|
|
454
|
+
if (sidebarProject) sidebarProject.textContent = currentProject || 'no project';
|
|
365
455
|
}
|
|
366
456
|
|
|
367
457
|
async function loadContext() {
|
|
@@ -708,10 +798,13 @@ function showEvalModal(e) {
|
|
|
708
798
|
|
|
709
799
|
async function doAction(name) {
|
|
710
800
|
if (name === 'open-dir') {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
801
|
+
try {
|
|
802
|
+
const res = await fetchJSON('/api/open-dir', { method: 'POST' });
|
|
803
|
+
showToast('Opening ' + (res.path || 'data dir…'));
|
|
804
|
+
} catch {
|
|
805
|
+
const s = await fetchJSON('/api/status').catch(() => ({}));
|
|
806
|
+
showToast('Data dir: ' + (s.doravalRoot || s.doravalDir || '~/.doraval'));
|
|
807
|
+
}
|
|
715
808
|
return;
|
|
716
809
|
}
|
|
717
810
|
if (name === 'sync' || name === 'update') {
|
|
@@ -842,9 +935,51 @@ function setupCaptureKeys() {
|
|
|
842
935
|
setTimeout(() => title.focus(), 300);
|
|
843
936
|
}
|
|
844
937
|
|
|
938
|
+
// --- Sidebar navigation ---
|
|
939
|
+
|
|
940
|
+
function scrollToSection(id) {
|
|
941
|
+
const el = document.getElementById(id);
|
|
942
|
+
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
943
|
+
setActiveNav(id);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function setActiveNav(sectionId) {
|
|
947
|
+
const map = {
|
|
948
|
+
'section-capture': 'nav-capture',
|
|
949
|
+
'section-journal': 'nav-journal',
|
|
950
|
+
'section-context': 'nav-context',
|
|
951
|
+
'section-hooks': 'nav-hooks',
|
|
952
|
+
'section-evals': 'nav-evals',
|
|
953
|
+
};
|
|
954
|
+
document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active'));
|
|
955
|
+
const navId = map[sectionId];
|
|
956
|
+
if (navId) {
|
|
957
|
+
const navEl = document.getElementById(navId);
|
|
958
|
+
if (navEl) navEl.classList.add('active');
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function setupSidebarObserver() {
|
|
963
|
+
const sections = ['section-capture', 'section-journal', 'section-context', 'section-hooks', 'section-evals'];
|
|
964
|
+
const observer = new IntersectionObserver((entries) => {
|
|
965
|
+
for (const entry of entries) {
|
|
966
|
+
if (entry.isIntersecting) {
|
|
967
|
+
setActiveNav(entry.target.id);
|
|
968
|
+
break;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}, { threshold: 0.2 });
|
|
972
|
+
|
|
973
|
+
sections.forEach(id => {
|
|
974
|
+
const el = document.getElementById(id);
|
|
975
|
+
if (el) observer.observe(el);
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
|
|
845
979
|
async function boot() {
|
|
846
980
|
setupSlider();
|
|
847
981
|
setupCaptureKeys();
|
|
982
|
+
setupSidebarObserver();
|
|
848
983
|
|
|
849
984
|
await loadStatus();
|
|
850
985
|
await Promise.all([loadContext(), loadEntries(), loadHooks(), loadEvals()]);
|
|
@@ -876,8 +1011,16 @@ window.doAction = doAction;
|
|
|
876
1011
|
window.closeModal = closeModal;
|
|
877
1012
|
window.clearForm = clearForm;
|
|
878
1013
|
window.updateAgentPreview = updateAgentPreview;
|
|
1014
|
+
window.scrollToSection = scrollToSection;
|
|
879
1015
|
|
|
880
|
-
boot().catch(
|
|
1016
|
+
boot().catch((err) => {
|
|
1017
|
+
const bodies = ['entries', 'context', 'evals', 'hooks'];
|
|
1018
|
+
bodies.forEach(id => {
|
|
1019
|
+
const el = document.getElementById(id);
|
|
1020
|
+
if (el) el.innerHTML = '<div class="text-red-400/70 text-xs py-2">Could not load — is the dora server still running?</div>';
|
|
1021
|
+
});
|
|
1022
|
+
console.error(err);
|
|
1023
|
+
});
|
|
881
1024
|
</script>
|
|
882
1025
|
</body>
|
|
883
1026
|
</html>
|