Hermes Web UI — Sprints 11-14: multi-provider models, settings, session QoL, alerts, polish
Sprint 11 (v0.13): multi-provider model support, streaming smoothness - Dynamic model dropdown populated from configured API keys (OpenAI, Anthropic, Google, DeepSeek, GLM, Kimi, MiniMax, OpenRouter, Nous Portal) - Scroll pinning during streaming (no forced scroll when user has scrolled up) - All route handlers extracted to api/routes.py (server.py now ~76 lines) Sprint 12 (v0.14): settings panel, SSE reconnect, session QoL - Settings panel (gear icon) -- persist default model and workspace server-side - SSE auto-reconnect on network blips - Pin/star sessions to top of sidebar - Import session from JSON export Sprint 13 (v0.15): cron alerts, background errors, session duplicate, tab title - Cron completion alerts: toast per completion + unread badge on Tasks tab - Background agent error banner when a non-active session errors mid-stream - Session duplicate button - Browser tab title reflects active session name Sprint 14 (v0.16): Mermaid diagrams, file ops, session archive/tags, timestamps - Mermaid diagram rendering inline (dark theme, lazy CDN load) - File rename (double-click in file tree) and create folder - Session archive (hide without deleting, toggle to show) - Session tags -- #hashtag in title becomes colored chip + click-to-filter - Message timestamps (HH:MM on hover, full date as tooltip) Test suite: 224 tests across 14 sprint files + regression gate, 0 failures.
This commit is contained in:
@@ -24,6 +24,24 @@ $('btnExportJSON').onclick=()=>{
|
||||
const a=document.createElement('a');a.href=url;
|
||||
a.download=`hermes-${S.session.session_id}.json`;a.click();
|
||||
};
|
||||
$('btnImportJSON').onclick=()=>$('importFileInput').click();
|
||||
$('importFileInput').onchange=async(e)=>{
|
||||
const file=e.target.files[0];
|
||||
if(!file)return;
|
||||
e.target.value='';
|
||||
try{
|
||||
const text=await file.text();
|
||||
const data=JSON.parse(text);
|
||||
const res=await api('/api/session/import',{method:'POST',body:JSON.stringify(data)});
|
||||
if(res.ok&&res.session){
|
||||
await loadSession(res.session.session_id);
|
||||
await renderSessionList();
|
||||
showToast('Session imported');
|
||||
}
|
||||
}catch(err){
|
||||
showToast('Import failed: '+(err.message||'Invalid JSON'));
|
||||
}
|
||||
};
|
||||
// btnRefreshFiles is now panel-icon-btn in header (see HTML)
|
||||
$('btnClearPreview').onclick=()=>{
|
||||
$('previewArea').classList.remove('visible');
|
||||
@@ -50,6 +68,9 @@ document.addEventListener('keydown',async e=>{
|
||||
if(!S.busy){await newSession();await renderSessionList();$('msg').focus();}
|
||||
}
|
||||
if(e.key==='Escape'){
|
||||
// Close settings overlay if open
|
||||
const settingsOverlay=$('settingsOverlay');
|
||||
if(settingsOverlay&&settingsOverlay.style.display!=='none'){toggleSettings();return;}
|
||||
// Close workspace dropdown
|
||||
closeWsDropdown();
|
||||
// Clear session search
|
||||
@@ -130,6 +151,8 @@ document.querySelectorAll('.suggestion').forEach(btn=>{
|
||||
})();
|
||||
|
||||
(async()=>{
|
||||
// Fetch available models from server and populate dropdown dynamically
|
||||
await populateModelDropdown();
|
||||
// Restore last-used model preference
|
||||
const savedModel=localStorage.getItem('hermes-webui-model');
|
||||
if(savedModel && $('modelSelect')){
|
||||
|
||||
Reference in New Issue
Block a user