From 30bac15b6ef4ad378da0c897ff6aa5dcefbfcb21 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Wed, 12 Aug 2026 08:47:17 +0200 Subject: [PATCH] Open toolbar flyout menus downwards when there is room (v1.20.18) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Identify/Cast flyouts were hard-anchored to open above their trigger, which suited the casting panel's bottom footer but clipped them off the top of the screen in Studio, where the same toolbar sits near the top — the first two entries were unreachable. They now pick the side with space, prefer opening downwards, stay clamped inside the viewport and scroll if taller than it. Also reorders the Identify menu to the sequence the passes should run in, with the one-click Auto-repair cast first. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 6 ++++++ VERSION | 2 +- static/dist/main.min.js | 4 ++-- static/index.html | 6 +++--- static/js/audiobook.js | 18 ++++++++++++++++-- static/style.css | 1 + 6 files changed, 29 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ce0c9..247765c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi - **Emotion instructions are now always written in English**, even for non-English voices (the spoken text and the native-accent clause stay in the book's own language). Confirmed by controlled A/B testing — same line, same voice, only the instruct language varying — that Qwen3-TTS follows English emotion instructions far more reliably: German instructs produced barely-differentiated output, while English instructs yield a clean, correctly-ordered prosodic gradient (whisper 128 Hz → sad 142 → neutral 179 → scared 203 → happy 225 → angry 269 Hz), with sensible duration changes too (sad slowest, scared fastest). - **Fish-Speech generation parameters (`temperature` / `top_p` / `repetition_penalty`) were never forwarded.** Every Fish-Speech line synthesized at the server's fixed defaults, ignoring the app's per-backend stability settings — the only backend not routed through the shared `_apply_tts_extra_params` helper. +## [1.20.18] — 2026-08-12 + +### Fixed +- **Toolbar menus opened upwards and were clipped off the top of the screen in Studio.** The Identify / Cast / Cast-view flyouts were hard-anchored to open above their button, which was right when those buttons lived in the casting panel's footer at the bottom of the page — but in Studio the same toolbar sits near the top, so the menu ran off-screen and its first entries were unreachable (confirmed live: "Auto-repair cast" and "Identify all characters" were both hidden). The menu now opens downwards whenever there is room, falls back to upwards when there isn't, is clamped inside the viewport, and scrolls if it is ever taller than the screen. +- Identify Characters menu order now matches the order the passes should be run in, with the one-click **Auto-repair cast** first. + ## [1.20.16] — 2026-08-12 ### Added diff --git a/VERSION b/VERSION index a46bb8e..41450c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.20.16 +1.20.18 diff --git a/static/dist/main.min.js b/static/dist/main.min.js index 02204f7..40104fe 100644 --- a/static/dist/main.min.js +++ b/static/dist/main.min.js @@ -1192,7 +1192,7 @@ ${lines.trim()}
${escHtml(message)}
${withProgress?'
':""} - `,document.body.appendChild(ov),ov.setProgress=(done,total)=>{const fill=ov.querySelector(".ab-busy-fill");fill&&total&&(fill.style.width=Math.round(done/total*100)+"%");const msgEl=ov.querySelector(".ab-busy-msg");msgEl&&(msgEl.textContent=`${message} (${done} / ${total})`)},ov}let _abFootMenuEl=null;function _abCloseFootMenu(){var _a2;_abFootMenuEl&&((_a2=document.getElementById(_abFootMenuEl.dataset.forId))==null||_a2.classList.remove("is-menu-open"),_abFootMenuEl.remove(),_abFootMenuEl=null,document.removeEventListener("click",_abFootMenuOutside,!0),document.removeEventListener("keydown",_abFootMenuKey,!0))}function _abFootMenuOutside(e){_abFootMenuEl&&!_abFootMenuEl.contains(e.target)&&!e.target.closest(".ab-foot-trigger")&&_abCloseFootMenu()}function _abFootMenuKey(e){e.key==="Escape"&&_abCloseFootMenu()}function _abToggleFootMenu(triggerBtn,items){const reopening=_abFootMenuEl&&_abFootMenuEl.dataset.forId===triggerBtn.id;if(_abCloseFootMenu(),reopening)return;const el=document.createElement("div");el.className="ab-foot-menu",el.dataset.forId=triggerBtn.id,el.innerHTML=items.map((it,i)=>it.divider?'
':``).join(""),document.body.appendChild(el);const rect=triggerBtn.getBoundingClientRect();el.style.left=Math.max(8,Math.min(rect.left,window.innerWidth-el.offsetWidth-8))+"px",el.style.bottom=window.innerHeight-rect.top+6+"px",el.querySelectorAll(".ab-foot-menu-item").forEach(btn=>{btn.addEventListener("click",()=>{var _a2;const it=items[Number(btn.dataset.idx)];_abCloseFootMenu(),(_a2=it==null?void 0:it.onClick)==null||_a2.call(it)})}),_abFootMenuEl=el,triggerBtn.classList.add("is-menu-open"),setTimeout(()=>{document.addEventListener("click",_abFootMenuOutside,!0),document.addEventListener("keydown",_abFootMenuKey,!0)},0)}function _abOpenRecastSelectPopup(existingChars){const ov=document.createElement("div");ov.className="audiobook-overlay";const selected=new Set;let sort="lines",filter="";try{sort=localStorage.getItem("ttsvc_ab_recast_sort")||"lines"}catch{}const rowHtml=c=>{var _a2;const lineCount=((_a2=c==null?void 0:c.sheet)==null?void 0:_a2.line_count)||0,color=_abRecordColor(c,c.name),avatar=c!=null&&c.image?``:`${escHtml(((c==null?void 0:c.name)||"?")[0].toUpperCase())}`;return`