mirror of
https://github.com/wahyd4/links.git
synced 2026-08-08 21:04:53 +10:00
jbot: fix voice button visibility - chat-panel height overflow
.chat-panel was using height:100% which consumed all right-col space, pushing VoiceButton out of view (clipped by overflow:hidden). Fix: switch .chat-panel to flex:1 + min-height:0 so it participates in flex layout and leaves room for the VoiceButton below it. Also moved VoiceButton component outside App() to avoid React component-identity churn on every render. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
@@ -272,7 +272,8 @@ kbd {
|
||||
.chat-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 14px 18px;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
+31
-33
@@ -45,41 +45,39 @@ function speakText(text) {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Hold-to-talk voice button (defined outside App to keep stable identity) ──
|
||||
function VoiceButton({ phase, onStart, onEnd }) {
|
||||
const busy = phase === 'thinking' || phase === 'speaking';
|
||||
let label = '🎙 按住说话';
|
||||
let mod = '';
|
||||
if (phase === 'acquiring') { label = '⏳ 准备中…'; mod = ' vbtn--acquiring'; }
|
||||
else if (phase === 'listening') { label = '🔴 松开发送'; mod = ' vbtn--recording'; }
|
||||
else if (phase === 'thinking') { label = '⚙ 思考中…'; mod = ' vbtn--busy'; }
|
||||
else if (phase === 'speaking') { label = '🔊 播放中…'; mod = ' vbtn--busy'; }
|
||||
|
||||
const startEvt = (e) => { e.preventDefault(); if (!busy) onStart(); };
|
||||
const endEvt = (e) => { e.preventDefault(); onEnd(); };
|
||||
|
||||
return (
|
||||
<div className="vbtn-wrap">
|
||||
<button
|
||||
className={`vbtn${mod}`}
|
||||
disabled={busy}
|
||||
onMouseDown={startEvt}
|
||||
onMouseUp={endEvt}
|
||||
onMouseLeave={endEvt}
|
||||
onTouchStart={startEvt}
|
||||
onTouchEnd={endEvt}
|
||||
onTouchCancel={endEvt}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
// ── VoiceButton: WeChat-style hold-to-record ──────────────
|
||||
// Placed here so it shares App's handleSpaceDown/handleSpaceUp via closure.
|
||||
function VoiceButton({ phase, onStart, onEnd }) {
|
||||
const busy = phase === 'thinking' || phase === 'speaking';
|
||||
let label = '🎙 按住说话';
|
||||
let mod = '';
|
||||
if (phase === 'acquiring') { label = '⏳ 准备中…'; mod = ' vbtn--acquiring'; }
|
||||
else if (phase === 'listening') { label = '🔴 松开发送'; mod = ' vbtn--recording'; }
|
||||
else if (phase === 'thinking') { label = '⚙ 思考中…'; mod = ' vbtn--busy'; }
|
||||
else if (phase === 'speaking') { label = '🔊 播放中…'; mod = ' vbtn--busy'; }
|
||||
|
||||
const startEvt = (e) => { e.preventDefault(); if (!busy) onStart(); };
|
||||
const endEvt = (e) => { e.preventDefault(); onEnd(); };
|
||||
|
||||
return (
|
||||
<div className="vbtn-wrap">
|
||||
<button
|
||||
className={`vbtn${mod}`}
|
||||
disabled={busy}
|
||||
onMouseDown={startEvt}
|
||||
onMouseUp={endEvt}
|
||||
onMouseLeave={endEvt}
|
||||
onTouchStart={startEvt}
|
||||
onTouchEnd={endEvt}
|
||||
onTouchCancel={endEvt}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Expression / auto-rotate state ───────────────────────────
|
||||
const [exprId, setExprId] = useState(ALL_AUTO_IDS[0]);
|
||||
const [autoRotate, setAutoRotate] = useState(true);
|
||||
|
||||
Reference in New Issue
Block a user