fix: reasoning chip dropdown visible + monochrome SVG icon + /btw answer preserved (closes #933) (#934)
* fix: reasoning chip dropdown visible + SVG icon + /btw answer no longer wiped (closes #933) * fix(ui): resize handler symmetry + lock regressions for PR #934 fixes Two small additions on top of the core PR: 1. Resize handler now re-positions the reasoning dropdown when the window resizes while it's open, matching the existing model-dropdown branch. Without this, resizing while the dropdown is open leaves it aligned to the pre-resize chip position — fine in practice (most resizes close the dropdown via the global click handler) but inconsistent with the model-dropdown sibling. 2. Regression test file tests/test_reasoning_chip_btw_fixes.py with 10 tests locking all four fixes in place so they can't silently regress: - Dropdown sits OUTSIDE .composer-left (so overflow-y: hidden can't clip it) - Dropdown is grouped with the other composer-level dropdowns - Chip button contains stroke="currentColor" SVG (not a 🧠 emoji) - _applyReasoningChip() body doesn't include 🧠 - cmdReasoning calls _applyReasoningChip(eff) directly with the server-confirmed effort, not syncReasoningChip() (stale cache) - _streamDone flag declared, set in done handler, checked in onerror - _ensureBtwRow() called in done handler (creates bubble when no tokens arrive) - resize handler re-positions composerReasoningDropdown Full suite: 2056 passed, 0 failed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -654,8 +654,8 @@ function cmdReasoning(args){
|
||||
api('/api/reasoning',{method:'POST',body:JSON.stringify({effort:arg})})
|
||||
.then(function(st){
|
||||
const eff=(st && st.reasoning_effort)||arg;
|
||||
showToast(BRAIN+' Reasoning effort set to '+eff+' (saved; applies to next turn)');
|
||||
if(typeof syncReasoningChip==='function') syncReasoningChip();
|
||||
showToast('Reasoning effort set to '+eff+' (saved; applies to next turn)');
|
||||
if(typeof _applyReasoningChip==='function') _applyReasoningChip(eff);
|
||||
})
|
||||
.catch(function(e){
|
||||
showToast(BRAIN+' Failed to set effort: '+(e && e.message ? e.message : arg));
|
||||
|
||||
@@ -348,18 +348,10 @@
|
||||
</div>
|
||||
<div class="composer-reasoning-wrap" id="composerReasoningWrap" style="display:none">
|
||||
<button class="composer-reasoning-chip" id="composerReasoningChip" type="button" onclick="toggleReasoningDropdown()" title="Reasoning effort level">
|
||||
<span class="composer-reasoning-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/><line x1="9" y1="21" x2="15" y2="21"/></svg></span>
|
||||
<span class="composer-reasoning-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96-.46 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96-.46 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/></svg></span>
|
||||
<span class="composer-reasoning-label" id="composerReasoningLabel"></span>
|
||||
<span class="composer-reasoning-chevron" aria-hidden="true"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
||||
</button>
|
||||
<div class="composer-reasoning-dropdown" id="composerReasoningDropdown">
|
||||
<div class="reasoning-option" data-effort="none">None</div>
|
||||
<div class="reasoning-option" data-effort="minimal">Minimal</div>
|
||||
<div class="reasoning-option" data-effort="low">Low</div>
|
||||
<div class="reasoning-option" data-effort="medium">Medium</div>
|
||||
<div class="reasoning-option" data-effort="high">High</div>
|
||||
<div class="reasoning-option" data-effort="xhigh">Extra High</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="composer-model-wrap">
|
||||
<button class="composer-model-chip" id="composerModelChip" type="button" onclick="toggleModelDropdown()" title="Conversation model">
|
||||
@@ -418,6 +410,14 @@
|
||||
</div>
|
||||
<div class="profile-dropdown" id="profileDropdown"></div>
|
||||
<div class="ws-dropdown ws-dropdown-footer" id="composerWsDropdown"></div>
|
||||
<div class="composer-reasoning-dropdown" id="composerReasoningDropdown">
|
||||
<div class="reasoning-option" data-effort="none">None</div>
|
||||
<div class="reasoning-option" data-effort="minimal">Minimal</div>
|
||||
<div class="reasoning-option" data-effort="low">Low</div>
|
||||
<div class="reasoning-option" data-effort="medium">Medium</div>
|
||||
<div class="reasoning-option" data-effort="high">High</div>
|
||||
<div class="reasoning-option" data-effort="xhigh">Extra High</div>
|
||||
</div>
|
||||
<div class="model-dropdown" id="composerModelDropdown"></div>
|
||||
</div>
|
||||
<div class="upload-bar-wrap" id="uploadBarWrap"><div class="upload-bar" id="uploadBar"></div></div>
|
||||
|
||||
@@ -1332,6 +1332,7 @@ function attachBtwStream(parentSid, streamId, question){
|
||||
const src=new EventSource('/api/stream?stream_id='+encodeURIComponent(streamId));
|
||||
let answer='';
|
||||
let btwRow=null;
|
||||
let _streamDone=false;
|
||||
function _ensureBtwRow(){
|
||||
if(btwRow&&btwRow.isConnected) return;
|
||||
const inner=$('msgInner');
|
||||
@@ -1363,10 +1364,12 @@ function attachBtwStream(parentSid, streamId, question){
|
||||
});
|
||||
src.addEventListener('done',e=>{
|
||||
src.close();
|
||||
_streamDone=true;
|
||||
try{
|
||||
const d=JSON.parse(e.data);
|
||||
if(d.answer&&!answer) answer=d.answer;
|
||||
}catch(_){}
|
||||
_ensureBtwRow();
|
||||
if(btwRow&&btwRow.isConnected){
|
||||
const ansEl=btwRow.querySelector('.msg-btw-answer');
|
||||
if(ansEl) ansEl.innerHTML=renderMd(answer||t('btw_no_answer'));
|
||||
@@ -1375,6 +1378,7 @@ function attachBtwStream(parentSid, streamId, question){
|
||||
});
|
||||
src.addEventListener('apperror',e=>{
|
||||
src.close();
|
||||
_streamDone=true;
|
||||
try{
|
||||
const d=JSON.parse(e.data);
|
||||
showToast(t('btw_failed')+(d.message||''));
|
||||
@@ -1382,7 +1386,7 @@ function attachBtwStream(parentSid, streamId, question){
|
||||
if(btwRow&&btwRow.isConnected) btwRow.remove();
|
||||
});
|
||||
src.addEventListener('stream_end',()=>{src.close();});
|
||||
src.onerror=()=>{src.close();if(btwRow&&btwRow.isConnected) btwRow.remove();};
|
||||
src.onerror=()=>{src.close();if(!_streamDone&&btwRow&&btwRow.isConnected) btwRow.remove();};
|
||||
}
|
||||
|
||||
// ── /background task tracking ────────────────────────────────────────────────
|
||||
|
||||
@@ -599,7 +599,7 @@
|
||||
.composer-reasoning-chip.active{color:var(--text);background:var(--accent-bg);border-color:var(--accent-bg);}
|
||||
.composer-reasoning-icon,.composer-reasoning-chevron{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;line-height:1;}
|
||||
.composer-reasoning-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
||||
.composer-reasoning-dropdown{display:none;position:absolute;bottom:calc(100% + 6px);left:0;background:var(--surface);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.15);min-width:140px;z-index:100;padding:4px;animation:dropdown-in .12s ease-out;}
|
||||
.composer-reasoning-dropdown{display:none;position:absolute;bottom:calc(100% + 4px);left:0;background:var(--surface);border:1px solid var(--border2);border-radius:10px;box-shadow:0 -4px 24px rgba(0,0,0,.4);min-width:140px;z-index:200;padding:4px;animation:dropdown-in .12s ease-out;}
|
||||
.composer-reasoning-dropdown.open{display:block;}
|
||||
.reasoning-option{padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;color:var(--text);white-space:nowrap;transition:background-color .1s;}
|
||||
.reasoning-option:hover{background:var(--hover-bg);}
|
||||
|
||||
22
static/ui.js
22
static/ui.js
@@ -406,6 +406,12 @@ document.addEventListener('click',e=>{
|
||||
window.addEventListener('resize',()=>{
|
||||
const dd=$('composerModelDropdown');
|
||||
if(dd&&dd.classList.contains('open')) _positionModelDropdown();
|
||||
// Keep the reasoning dropdown aligned under its chip when the window
|
||||
// resizes while open — same pattern as the model dropdown above.
|
||||
const rdd=$('composerReasoningDropdown');
|
||||
if(rdd&&rdd.classList.contains('open')&&typeof _positionReasoningDropdown==='function'){
|
||||
_positionReasoningDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
// ── Reasoning effort chip ────────────────────────────────────────────────────
|
||||
@@ -418,7 +424,7 @@ function _applyReasoningChip(eff){
|
||||
if(!wrap||!label) return;
|
||||
if(!eff||eff==='none'){wrap.style.display='none';return;}
|
||||
wrap.style.display='';
|
||||
label.textContent='🧠 '+eff;
|
||||
label.textContent=eff;
|
||||
_highlightReasoningOption(eff);
|
||||
}
|
||||
|
||||
@@ -452,9 +458,23 @@ function toggleReasoningDropdown(){
|
||||
closeModelDropdown();
|
||||
_highlightReasoningOption(_currentReasoningEffort);
|
||||
dd.classList.add('open');
|
||||
_positionReasoningDropdown();
|
||||
chip.classList.add('active');
|
||||
}
|
||||
|
||||
function _positionReasoningDropdown(){
|
||||
const dd=$('composerReasoningDropdown');
|
||||
const chip=$('composerReasoningChip');
|
||||
const footer=document.querySelector('.composer-footer');
|
||||
if(!dd||!chip||!footer) return;
|
||||
const chipRect=chip.getBoundingClientRect();
|
||||
const footerRect=footer.getBoundingClientRect();
|
||||
let left=chipRect.left-footerRect.left;
|
||||
const maxLeft=Math.max(0,footer.clientWidth-dd.offsetWidth);
|
||||
left=Math.max(0,Math.min(left,maxLeft));
|
||||
dd.style.left=`${left}px`;
|
||||
}
|
||||
|
||||
function closeReasoningDropdown(){
|
||||
const dd=$('composerReasoningDropdown');
|
||||
const chip=$('composerReasoningChip');
|
||||
|
||||
Reference in New Issue
Block a user