fix: batch tag queries, inline edit, delete response, SPA catch-all, link glyph

- Fix N+1 tag query in List() with batched IN clause
- Add inline body editing in web detail pane (dblclick or e key)
- Delete API returns {result: "soft"|"hard"} with 200 instead of 204
- SPA handler serves index.html for all extensionless paths
- Link glyph changed from emoji 🔗 to unicode ↗ for terminal alignment
- Capture bar contrast and hover glow increased
- Comment on load-bearing "--" in root.go
This commit is contained in:
2026-05-14 12:37:13 -04:00
parent 5b0d0a8f33
commit 03094706c3
9 changed files with 152 additions and 23 deletions
+46 -4
View File
@@ -4,7 +4,7 @@
const GLYPHS = {
note: '◦', todo: '▸', event: '◇',
snippet: '◆', template: '◈', checklist: '☐',
decision: '⚖', link: '🔗',
decision: '⚖', link: '',
};
const GLYPH_CLASSES = {
@@ -278,11 +278,14 @@
<span class="detail-id">${shortId}</span>
${e.time_anchor ? `<span class="entity-time">@${e.time_anchor}</span>` : ''}
</div>
<div class="detail-body">${escHtml(e.body)}</div>
<div class="detail-body" data-id="${e.id}">${escHtml(e.body)}</div>
${tags ? `<div class="detail-tags">${tags}</div>` : ''}
${cardContent}
<div class="detail-actions">${actions}</div>
`;
const bodyEl = pane.querySelector('.detail-body');
if (bodyEl) bodyEl.addEventListener('dblclick', startEditBody);
}
function renderCardContent(e) {
@@ -334,6 +337,40 @@
}
}
// ========== Inline edit ==========
function startEditBody() {
const e = state.entities[state.selectedIndex];
if (!e) return;
const el = $(`.detail-body[data-id="${e.id}"]`);
if (!el || el.tagName === 'TEXTAREA') return;
const ta = document.createElement('textarea');
ta.className = 'detail-body-edit';
ta.value = e.body;
el.replaceWith(ta);
ta.focus();
ta.setSelectionRange(ta.value.length, ta.value.length);
async function save() {
const newBody = ta.value.trim();
if (newBody && newBody !== e.body) {
await api.updateEntity(e.id, { body: newBody });
await loadEntities();
const idx = state.entities.findIndex(x => x.id === e.id);
if (idx >= 0) selectEntity(idx);
} else {
renderDetailPane();
}
}
ta.addEventListener('blur', save);
ta.addEventListener('keydown', (ev) => {
if (ev.key === 'Enter' && ev.ctrlKey) { ev.preventDefault(); ta.removeEventListener('blur', save); save(); }
if (ev.key === 'Escape') { ev.preventDefault(); ta.removeEventListener('blur', save); renderDetailPane(); }
});
}
// ========== Actions ==========
function selectEntity(idx) {
@@ -498,8 +535,9 @@
const captureInput = $('#capture-input');
document.addEventListener('keydown', (ev) => {
if (document.activeElement === captureInput) {
if (ev.key === 'Escape') captureInput.blur();
if (document.activeElement === captureInput ||
document.activeElement.classList.contains('detail-body-edit')) {
if (ev.key === 'Escape') document.activeElement.blur();
return;
}
@@ -544,6 +582,10 @@
}
break;
}
case 'e': {
startEditBody();
break;
}
case '1': switchView('stream'); break;
case '2': switchView('cards'); break;
}
+1 -1
View File
@@ -51,7 +51,7 @@
<span>decision</span>
</button>
<button data-type="link" class="type-btn">
<span class="type-glyph">🔗</span>
<span class="type-glyph"></span>
<span>link</span>
</button>
</div>
+37 -3
View File
@@ -92,22 +92,28 @@ nav {
#capture-input {
width: 100%;
background: var(--bg);
border: 1px solid var(--border);
border: 1px solid var(--text-muted);
color: var(--text);
padding: 8px 12px;
border-radius: var(--radius);
font-family: var(--font-mono);
font-size: 13px;
outline: none;
transition: border-color 0.15s;
transition: border-color 0.15s, box-shadow 0.15s;
}
#capture-input:hover {
border-color: var(--accent-dim);
box-shadow: 0 0 0 1px var(--accent-dim);
}
#capture-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px var(--accent);
}
#capture-input::placeholder {
color: var(--text-muted);
color: var(--text-dim);
}
/* Main layout */
@@ -273,6 +279,34 @@ main {
margin-bottom: 16px;
white-space: pre-wrap;
word-break: break-word;
cursor: text;
border-radius: var(--radius);
padding: 4px 6px;
margin-left: -6px;
transition: background 0.1s;
}
.detail-body:hover {
background: var(--bg-hover);
}
.detail-body-edit {
display: block;
width: 100%;
min-height: 80px;
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.7;
margin-bottom: 16px;
padding: 6px 8px;
background: var(--bg);
color: var(--text);
border: 1px solid var(--accent);
border-radius: var(--radius);
outline: none;
resize: vertical;
white-space: pre-wrap;
word-break: break-word;
}
.detail-tags {