Merge 7ffab280c383f80172845d8e2f18eb50e8af2582 into 8a05c05596ca9ba0735dafd8e359885de4c2c969

This commit is contained in:
Ricky 2026-03-21 14:05:49 +08:00 committed by GitHub
commit 87b0b37ed1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 48 additions and 26 deletions

View File

@ -363,11 +363,17 @@
box-sizing: border-box;
}
.agent-chat__input-wrap {
position: relative;
margin: 0 18px 14px;
flex-shrink: 0;
}
.agent-chat__input {
position: relative;
display: flex;
flex-direction: column;
margin: 0 18px 14px;
margin: 0;
padding: 0;
background: var(--card);
border: 1px solid var(--border);

View File

@ -513,7 +513,7 @@
font-size: 14px;
}
.agent-chat__input {
.agent-chat__input-wrap {
margin: 0 8px 10px;
}

View File

@ -952,6 +952,16 @@ export function renderChat(props: ChatProps) {
</div>
`;
/** After arrow-key navigation, scroll the active slash-menu item into view. */
const scrollSlashMenuActiveIntoView = () => {
requestAnimationFrame(() => {
const active = document.querySelector(".agent-chat__input-wrap .slash-menu-item--active");
if (active) {
active.scrollIntoView({ block: "nearest" });
}
});
};
const handleKeyDown = (e: KeyboardEvent) => {
// Slash menu navigation — arg mode
if (vs.slashMenuOpen && vs.slashMenuMode === "args" && vs.slashMenuArgItems.length > 0) {
@ -961,11 +971,13 @@ export function renderChat(props: ChatProps) {
e.preventDefault();
vs.slashMenuIndex = (vs.slashMenuIndex + 1) % len;
requestUpdate();
scrollSlashMenuActiveIntoView();
return;
case "ArrowUp":
e.preventDefault();
vs.slashMenuIndex = (vs.slashMenuIndex - 1 + len) % len;
requestUpdate();
scrollSlashMenuActiveIntoView();
return;
case "Tab":
e.preventDefault();
@ -992,11 +1004,13 @@ export function renderChat(props: ChatProps) {
e.preventDefault();
vs.slashMenuIndex = (vs.slashMenuIndex + 1) % len;
requestUpdate();
scrollSlashMenuActiveIntoView();
return;
case "ArrowUp":
e.preventDefault();
vs.slashMenuIndex = (vs.slashMenuIndex - 1 + len) % len;
requestUpdate();
scrollSlashMenuActiveIntoView();
return;
case "Tab":
e.preventDefault();
@ -1182,34 +1196,35 @@ export function renderChat(props: ChatProps) {
}
<!-- Input bar -->
<div class="agent-chat__input">
<div class="agent-chat__input-wrap">
${renderSlashMenu(requestUpdate, props)}
${renderAttachmentPreview(props)}
<div class="agent-chat__input">
${renderAttachmentPreview(props)}
<input
type="file"
accept=${CHAT_ATTACHMENT_ACCEPT}
multiple
class="agent-chat__file-input"
@change=${(e: Event) => handleFileSelect(e, props)}
/>
<input
type="file"
accept=${CHAT_ATTACHMENT_ACCEPT}
multiple
class="agent-chat__file-input"
@change=${(e: Event) => handleFileSelect(e, props)}
/>
${vs.sttRecording && vs.sttInterimText ? html`<div class="agent-chat__stt-interim">${vs.sttInterimText}</div>` : nothing}
${vs.sttRecording && vs.sttInterimText ? html`<div class="agent-chat__stt-interim">${vs.sttInterimText}</div>` : nothing}
<textarea
${ref((el) => el && adjustTextareaHeight(el as HTMLTextAreaElement))}
.value=${props.draft}
dir=${detectTextDirection(props.draft)}
?disabled=${!props.connected}
@keydown=${handleKeyDown}
@input=${handleInput}
@paste=${(e: ClipboardEvent) => handlePaste(e, props)}
placeholder=${vs.sttRecording ? "Listening..." : placeholder}
rows="1"
></textarea>
<textarea
${ref((el) => el && adjustTextareaHeight(el as HTMLTextAreaElement))}
.value=${props.draft}
dir=${detectTextDirection(props.draft)}
?disabled=${!props.connected}
@keydown=${handleKeyDown}
@input=${handleInput}
@paste=${(e: ClipboardEvent) => handlePaste(e, props)}
placeholder=${vs.sttRecording ? "Listening..." : placeholder}
rows="1"
></textarea>
<div class="agent-chat__toolbar">
<div class="agent-chat__toolbar-left">
<div class="agent-chat__toolbar">
<div class="agent-chat__toolbar-left">
<button
class="agent-chat__input-btn"
@click=${() => {
@ -1324,7 +1339,8 @@ export function renderChat(props: ChatProps) {
</div>
</div>
</div>
</section>
</div>
</section>
`;
}