@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/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
- <div class="max-w-5xl mx-auto p-8">
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 class="flex items-center gap-4">
128
- <div class="flex items-center justify-center w-9 h-9 rounded-2xl bg-zinc-900 border border-zinc-800 text-xl">
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 &amp; 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"></div>
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"></div>
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"></div>
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 &amp; 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
- const s = await fetchJSON('/api/status');
712
- const root = s.doravalRoot || s.doravalDir || '~/.doraval';
713
- showToast('Data directory: ' + root);
714
- // optionally try to open, but browser security limits it
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(console.error);
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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hacksmith/doraval",
3
- "version": "0.2.48",
3
+ "version": "0.2.50",
4
4
  "author": "Saif",
5
5
  "repository": {
6
6
  "type": "git",