feat: session delete + touch-friendly message controls
Session delete:
- DELETE /sessions/{session_id} endpoint (chat.py + session_store.py)
- × button on each session item in the panel (hover-reveal on desktop)
- Clears UI if the active session is deleted
Touch accessibility:
- @media (hover: none) rule makes msg-actions always visible on touch devices
- msg-act-btn tap targets enlarged to 36px min-height, readable font size
- session-delete-btn also always visible and finger-sized on touch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ from pydantic import BaseModel
|
||||
from context_loader import load_context
|
||||
from llm_client import complete
|
||||
from session_logger import log_turn
|
||||
from session_store import load as load_session, save as save_session, list_all, generate_session_id
|
||||
from session_store import load as load_session, save as save_session, list_all, generate_session_id, delete as delete_session
|
||||
from config import settings
|
||||
import event_bus
|
||||
|
||||
@@ -143,6 +143,14 @@ async def list_sessions() -> dict:
|
||||
return {"sessions": list_all()}
|
||||
|
||||
|
||||
@router.delete("/sessions/{session_id}")
|
||||
async def delete_session_endpoint(session_id: str) -> dict:
|
||||
found = delete_session(session_id)
|
||||
if not found:
|
||||
raise HTTPException(status_code=404, detail=f"Session {session_id} not found")
|
||||
return {"ok": True, "session_id": session_id}
|
||||
|
||||
|
||||
@router.put("/history/{session_id}")
|
||||
async def replace_history(session_id: str, req: HistoryUpdate) -> dict:
|
||||
"""Replace the full message list for a session (used by edit/delete UI)."""
|
||||
|
||||
@@ -69,6 +69,15 @@ def save(session_id: str, messages: list[dict]) -> None:
|
||||
}, indent=2))
|
||||
|
||||
|
||||
def delete(session_id: str) -> bool:
|
||||
"""Delete a session file. Returns True if it existed and was deleted."""
|
||||
path = _path(session_id)
|
||||
if not path.exists():
|
||||
return False
|
||||
path.unlink()
|
||||
return True
|
||||
|
||||
|
||||
def list_all() -> list[dict]:
|
||||
d = settings.sessions_path()
|
||||
if not d.exists():
|
||||
|
||||
@@ -183,6 +183,27 @@
|
||||
`${s.message_count} msgs · ${timeAgo(s.updated)}`
|
||||
);
|
||||
item.addEventListener('click', () => resumeSession(s.session_id));
|
||||
|
||||
const delBtn = document.createElement('button');
|
||||
delBtn.className = 'session-delete-btn';
|
||||
delBtn.textContent = '×';
|
||||
delBtn.title = 'Delete session';
|
||||
delBtn.addEventListener('click', async (e) => {
|
||||
e.stopPropagation();
|
||||
await fetch(`/sessions/${s.session_id}`, { method: 'DELETE' });
|
||||
if (sessionId === s.session_id) {
|
||||
sessionId = null;
|
||||
currentHistory = [];
|
||||
messagesEl.innerHTML = '';
|
||||
sessionEl.textContent = '';
|
||||
addMessage('system', 'Session deleted');
|
||||
}
|
||||
const res = await fetch('/sessions');
|
||||
const data = await res.json();
|
||||
renderPanel(data.sessions);
|
||||
});
|
||||
item.appendChild(delBtn);
|
||||
|
||||
sessionsPanel.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,23 @@
|
||||
.session-item:hover { background: var(--bg); }
|
||||
.session-item.new { color: var(--accent); justify-content: center; }
|
||||
|
||||
.session-delete-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
font-size: 1.1rem;
|
||||
line-height: 1;
|
||||
padding: 2px 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, color 0.15s;
|
||||
}
|
||||
.session-item:hover .session-delete-btn { opacity: 1; }
|
||||
.session-delete-btn:hover { color: #e06c75; }
|
||||
|
||||
.session-id {
|
||||
font-family: monospace;
|
||||
font-size: 0.85rem;
|
||||
@@ -1029,6 +1046,29 @@
|
||||
#note-type-btn { padding: 6px 10px; }
|
||||
}
|
||||
|
||||
/* ── Touch devices — no hover capability ─────────────────── */
|
||||
/* Always show message controls; make tap targets finger-sized */
|
||||
@media (hover: none) {
|
||||
.msg-actions {
|
||||
opacity: 1;
|
||||
padding: 4px 2px 2px;
|
||||
}
|
||||
|
||||
.msg-act-btn {
|
||||
font-size: 0.78rem;
|
||||
padding: 6px 12px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.session-delete-btn {
|
||||
opacity: 1;
|
||||
font-size: 1.3rem;
|
||||
padding: 4px 8px;
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 380px) {
|
||||
header .name { font-size: 1rem; }
|
||||
.header-emoji { font-size: 1.3rem; }
|
||||
|
||||
Reference in New Issue
Block a user