fix(mobile): adapt settings dialog and message controls for mobile screens (#919)
* fix(mobile): adapt settings dialog and message controls for mobile screens (#915) Co-authored-by: bsgdigital * fix(mobile): adapt settings dialog and message controls for mobile screens (v0.50.177, #915) Co-authored-by: bsgdigital --------- Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
This commit is contained in:
@@ -29,6 +29,11 @@
|
|||||||
workspace subtree) and never enumerate blocked system roots. (`api/routes.py`,
|
workspace subtree) and never enumerate blocked system roots. (`api/routes.py`,
|
||||||
`api/workspace.py`, `static/panels.js`, `static/style.css`) (partial for #616)
|
`api/workspace.py`, `static/panels.js`, `static/style.css`) (partial for #616)
|
||||||
|
|
||||||
|
## [v0.50.177] — 2026-04-23
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Settings dialog and message controls unusable on mobile** — three mobile usability fixes: (1) settings tab strip replaced by a native `<select>` dropdown on narrow viewports, panel goes full-width; (2) provider card Save/Remove buttons become icon-only on mobile so the API key input fills the available width; (3) message timestamps, copy, and edit buttons are always visible on touch screens (no hover state on mobile). (`static/index.html`, `static/panels.js`, `static/style.css`) Co-authored by @bsgdigital.
|
||||||
|
|
||||||
## [v0.50.176] — 2026-04-23
|
## [v0.50.176] — 2026-04-23
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -446,6 +446,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="settings-body">
|
<div class="settings-body">
|
||||||
<div class="settings-shell">
|
<div class="settings-shell">
|
||||||
|
<select id="settingsSectionDropdown" class="settings-section-dropdown" aria-label="Settings section" onchange="switchSettingsSection(this.value)">
|
||||||
|
<option value="conversation">Conversation</option>
|
||||||
|
<option value="appearance">Appearance</option>
|
||||||
|
<option value="preferences">Preferences</option>
|
||||||
|
<option value="providers">Providers</option>
|
||||||
|
<option value="system">System</option>
|
||||||
|
</select>
|
||||||
<div class="settings-tabs" role="tablist" aria-label="Hermes control center sections">
|
<div class="settings-tabs" role="tablist" aria-label="Hermes control center sections">
|
||||||
<button class="settings-tab active" id="settingsTabConversation" type="button" role="tab" aria-selected="true" aria-controls="settingsPaneConversation" onclick="switchSettingsSection('conversation')">
|
<button class="settings-tab active" id="settingsTabConversation" type="button" role="tab" aria-selected="true" aria-controls="settingsPaneConversation" onclick="switchSettingsSection('conversation')">
|
||||||
<svg class="settings-tab-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
<svg class="settings-tab-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||||||
|
|||||||
@@ -1291,6 +1291,9 @@ function switchSettingsSection(name){
|
|||||||
}
|
}
|
||||||
if(pane) pane.classList.toggle('active',active);
|
if(pane) pane.classList.toggle('active',active);
|
||||||
});
|
});
|
||||||
|
// Sync mobile dropdown
|
||||||
|
const dd=$('settingsSectionDropdown');
|
||||||
|
if(dd && dd.value!==section) dd.value=section;
|
||||||
// Lazy-load providers when the tab is opened
|
// Lazy-load providers when the tab is opened
|
||||||
if(section==='providers') loadProvidersPanel();
|
if(section==='providers') loadProvidersPanel();
|
||||||
}
|
}
|
||||||
@@ -1590,24 +1593,25 @@ function _buildProviderCard(p){
|
|||||||
}else{
|
}else{
|
||||||
const actions=document.createElement('div');
|
const actions=document.createElement('div');
|
||||||
actions.className='provider-card-actions';
|
actions.className='provider-card-actions';
|
||||||
actions.style.cssText='margin-top:6px;display:flex;gap:6px;align-items:center';
|
|
||||||
const input=document.createElement('input');
|
const input=document.createElement('input');
|
||||||
input.type='password';
|
input.type='password';
|
||||||
input.placeholder=p.has_key?t('providers_key_placeholder_replace'):t('providers_key_placeholder_new');
|
input.placeholder=p.has_key?t('providers_key_placeholder_replace'):t('providers_key_placeholder_new');
|
||||||
input.style.cssText='flex:1;padding:6px 8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:12px;font-family:monospace';
|
input.style.cssText='flex:1;min-width:0;padding:6px 8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:12px;font-family:monospace';
|
||||||
input.autocomplete='off';
|
input.autocomplete='off';
|
||||||
const saveBtn=document.createElement('button');
|
const saveBtn=document.createElement('button');
|
||||||
saveBtn.className='sm-btn provider-save-btn';
|
saveBtn.className='sm-btn provider-save-btn';
|
||||||
saveBtn.style.cssText='padding:5px 12px;font-size:12px;white-space:nowrap';
|
saveBtn.setAttribute('aria-label',t('providers_save'));
|
||||||
saveBtn.textContent=t('providers_save');
|
saveBtn.title=t('providers_save');
|
||||||
|
saveBtn.innerHTML='<svg class="provider-btn-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg><span class="provider-btn-label">'+t('providers_save')+'</span>';
|
||||||
saveBtn.onclick=()=>_saveProviderKey(p.id);
|
saveBtn.onclick=()=>_saveProviderKey(p.id);
|
||||||
actions.appendChild(input);
|
actions.appendChild(input);
|
||||||
actions.appendChild(saveBtn);
|
actions.appendChild(saveBtn);
|
||||||
if(p.has_key){
|
if(p.has_key){
|
||||||
const removeBtn=document.createElement('button');
|
const removeBtn=document.createElement('button');
|
||||||
removeBtn.className='sm-btn';
|
removeBtn.className='sm-btn provider-remove-btn';
|
||||||
removeBtn.style.cssText='padding:5px 10px;font-size:12px;color:var(--error);border-color:rgba(233,69,96,.25);white-space:nowrap';
|
removeBtn.setAttribute('aria-label',t('providers_remove'));
|
||||||
removeBtn.textContent=t('providers_remove');
|
removeBtn.title=t('providers_remove');
|
||||||
|
removeBtn.innerHTML='<svg class="provider-btn-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg><span class="provider-btn-label">'+t('providers_remove')+'</span>';
|
||||||
removeBtn.onclick=()=>_removeProviderKey(p.id);
|
removeBtn.onclick=()=>_removeProviderKey(p.id);
|
||||||
actions.appendChild(removeBtn);
|
actions.appendChild(removeBtn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -756,9 +756,6 @@
|
|||||||
.topbar-meta{display:none;}
|
.topbar-meta{display:none;}
|
||||||
.topbar-chips{flex-wrap:nowrap;gap:4px;overflow-x:auto;-webkit-overflow-scrolling:touch;}
|
.topbar-chips{flex-wrap:nowrap;gap:4px;overflow-x:auto;-webkit-overflow-scrolling:touch;}
|
||||||
.topbar-chips .chip,.topbar-chips .ws-chip,.topbar-chips button{font-size:11px!important;padding:4px 8px!important;white-space:nowrap;}
|
.topbar-chips .chip,.topbar-chips .ws-chip,.topbar-chips button{font-size:11px!important;padding:4px 8px!important;white-space:nowrap;}
|
||||||
.settings-shell{grid-template-columns:1fr;gap:0;}
|
|
||||||
.settings-tabs{flex-direction:row;overflow-x:auto;padding:10px 12px;border-right:none;border-bottom:1px solid var(--border);gap:6px;}
|
|
||||||
.settings-tab{flex-shrink:0;}
|
|
||||||
.settings-main{padding:18px 16px;}
|
.settings-main{padding:18px 16px;}
|
||||||
.hermes-action-grid{grid-template-columns:1fr;}
|
.hermes-action-grid{grid-template-columns:1fr;}
|
||||||
.messages-inner{padding:12px 10px 20px;}
|
.messages-inner{padding:12px 10px 20px;}
|
||||||
@@ -1485,6 +1482,7 @@ body.resizing{user-select:none;cursor:col-resize;}
|
|||||||
.settings-main{overflow-y:auto;padding:22px 24px;min-width:0;}
|
.settings-main{overflow-y:auto;padding:22px 24px;min-width:0;}
|
||||||
.settings-pane{display:none;}
|
.settings-pane{display:none;}
|
||||||
.settings-pane.active{display:block;}
|
.settings-pane.active{display:block;}
|
||||||
|
.settings-section-dropdown{display:none;}
|
||||||
.settings-section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px;}
|
.settings-section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px;}
|
||||||
.settings-section-title{font-size:13px;font-weight:700;letter-spacing:.01em;color:var(--text);}
|
.settings-section-title{font-size:13px;font-weight:700;letter-spacing:.01em;color:var(--text);}
|
||||||
.settings-section-meta{font-size:11px;color:var(--muted);margin-top:3px;line-height:1.5;}
|
.settings-section-meta{font-size:11px;color:var(--muted);margin-top:3px;line-height:1.5;}
|
||||||
@@ -1507,6 +1505,11 @@ body.resizing{user-select:none;cursor:col-resize;}
|
|||||||
.provider-card:hover{border-color:var(--border2);}
|
.provider-card:hover{border-color:var(--border2);}
|
||||||
.provider-card-name{font-weight:600;font-size:13px;}
|
.provider-card-name{font-weight:600;font-size:13px;}
|
||||||
.provider-card .sm-btn:disabled{opacity:.4;cursor:not-allowed;}
|
.provider-card .sm-btn:disabled{opacity:.4;cursor:not-allowed;}
|
||||||
|
.provider-card-actions{margin-top:6px;display:flex;gap:6px;align-items:center;width:100%;}
|
||||||
|
.provider-save-btn{padding:5px 12px;font-size:12px;white-space:nowrap;display:inline-flex;align-items:center;gap:5px;flex:none;}
|
||||||
|
.provider-remove-btn{padding:5px 10px;font-size:12px;color:var(--error);border-color:rgba(233,69,96,.25);white-space:nowrap;display:inline-flex;align-items:center;gap:5px;flex:none;}
|
||||||
|
.provider-btn-icon{flex-shrink:0;}
|
||||||
|
.provider-btn-label{}
|
||||||
|
|
||||||
/* ── Session pin indicator (inline, only when pinned) ── */
|
/* ── Session pin indicator (inline, only when pinned) ── */
|
||||||
.session-pin-indicator{
|
.session-pin-indicator{
|
||||||
@@ -1888,3 +1891,21 @@ body.resizing{user-select:none;cursor:col-resize;}
|
|||||||
.msg-row[data-role="user"] .msg-body { padding: 8px 12px; }
|
.msg-row[data-role="user"] .msg-body { padding: 8px 12px; }
|
||||||
.msg-row[data-error="1"] .msg-body { padding: 8px 12px; }
|
.msg-row[data-error="1"] .msg-body { padding: 8px 12px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mobile overrides — must live after all base rules to win the cascade without !important */
|
||||||
|
@media(max-width:640px){
|
||||||
|
/* Settings dialog: replace left tab strip with a dropdown, panel goes full-width */
|
||||||
|
.settings-shell{display:flex;flex-direction:column;gap:0;}
|
||||||
|
.settings-section-dropdown{display:block;margin:10px 14px;width:calc(100% - 28px);}
|
||||||
|
.settings-tabs{display:none;}
|
||||||
|
.settings-main{padding:18px 16px;width:100%;}
|
||||||
|
/* Provider buttons: icon-only on mobile, input fills remaining space */
|
||||||
|
.provider-save-btn,.provider-remove-btn{padding:7px;gap:0;flex-shrink:0;}
|
||||||
|
.provider-btn-label{display:none;}
|
||||||
|
|
||||||
|
/* Message controls: always visible on touch screens (no hover available) */
|
||||||
|
.msg-row[data-role="user"] .msg-foot,
|
||||||
|
.msg-row[data-role="assistant"] .msg-foot,
|
||||||
|
.assistant-turn .msg-foot{opacity:1;}
|
||||||
|
.msg-actions{opacity:1;}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user