fix(ui): move prompt mic beside expand control

This commit is contained in:
Shantur Rathore
2026-03-25 11:56:02 +00:00
parent fd529196fa
commit c064cea4cc
2 changed files with 70 additions and 48 deletions

View File

@@ -543,10 +543,54 @@ export default function PromptInput(props: PromptInputProps) {
autocomplete="off" autocomplete="off"
/> />
<div class="prompt-nav-buttons"> <div class="prompt-nav-buttons">
<ExpandButton <div class="prompt-nav-top-row">
expandState={expandState} <Show when={showVoiceInput()}>
onToggleExpand={handleExpandToggle} <button
/> type="button"
class={`prompt-voice-button prompt-nav-voice-button ${voiceInput.isRecording() ? "is-recording" : ""}`}
onPointerDown={(event) => {
event.preventDefault()
beginVoicePress(event)
}}
onPointerUp={(event) => {
event.preventDefault()
endVoicePress()
}}
onPointerCancel={() => endVoicePress()}
onLostPointerCapture={() => endVoicePress()}
onKeyDown={(event) => {
if (event.repeat) return
if (event.key !== " " && event.key !== "Enter") return
event.preventDefault()
beginVoicePress(event)
}}
onKeyUp={(event) => {
if (event.key !== " " && event.key !== "Enter") return
event.preventDefault()
endVoicePress()
}}
onBlur={() => endVoicePress()}
disabled={!voiceInput.isRecording() && (props.disabled || voiceInput.isTranscribing() || !voiceInput.canUseVoiceInput())}
aria-label={voiceInput.buttonTitle()}
title={voiceInput.buttonTitle()}
>
<Show
when={voiceInput.isRecording()}
fallback={
<Show when={voiceInput.isTranscribing()} fallback={<Mic class="h-4 w-4" aria-hidden="true" />}>
<Loader2 class="h-4 w-4 animate-spin" aria-hidden="true" />
</Show>
}
>
<span class="prompt-voice-timer">{formatVoiceTimer(voiceInput.elapsedMs())}</span>
</Show>
</button>
</Show>
<ExpandButton
expandState={expandState}
onToggleExpand={handleExpandToggle}
/>
</div>
<Show when={hasHistory()}> <Show when={hasHistory()}>
<button <button
type="button" type="button"
@@ -634,48 +678,6 @@ export default function PromptInput(props: PromptInputProps) {
</div> </div>
<div class="prompt-input-actions"> <div class="prompt-input-actions">
<Show when={showVoiceInput()}>
<button
type="button"
class={`prompt-voice-button ${voiceInput.isRecording() ? "is-recording" : ""}`}
onPointerDown={(event) => {
event.preventDefault()
beginVoicePress(event)
}}
onPointerUp={(event) => {
event.preventDefault()
endVoicePress()
}}
onPointerCancel={() => endVoicePress()}
onLostPointerCapture={() => endVoicePress()}
onKeyDown={(event) => {
if (event.repeat) return
if (event.key !== " " && event.key !== "Enter") return
event.preventDefault()
beginVoicePress(event)
}}
onKeyUp={(event) => {
if (event.key !== " " && event.key !== "Enter") return
event.preventDefault()
endVoicePress()
}}
onBlur={() => endVoicePress()}
disabled={!voiceInput.isRecording() && (props.disabled || voiceInput.isTranscribing() || !voiceInput.canUseVoiceInput())}
aria-label={voiceInput.buttonTitle()}
title={voiceInput.buttonTitle()}
>
<Show
when={voiceInput.isRecording()}
fallback={
<Show when={voiceInput.isTranscribing()} fallback={<Mic class="h-4 w-4" aria-hidden="true" />}>
<Loader2 class="h-4 w-4 animate-spin" aria-hidden="true" />
</Show>
}
>
<span class="prompt-voice-timer">{formatVoiceTimer(voiceInput.elapsedMs())}</span>
</Show>
</button>
</Show>
<button <button
type="button" type="button"
class="stop-button" class="stop-button"

View File

@@ -37,7 +37,7 @@
.prompt-input { .prompt-input {
@apply w-full pt-2.5 border text-sm resize-none outline-none transition-colors; @apply w-full pt-2.5 border text-sm resize-none outline-none transition-colors;
padding-inline-start: 0.75rem; padding-inline-start: 0.75rem;
padding-inline-end: 2.5rem; padding-inline-end: 5.5rem;
font-family: inherit; font-family: inherit;
background-color: var(--surface-base); background-color: var(--surface-base);
color: var(--text-primary); color: var(--text-primary);
@@ -91,11 +91,19 @@
bottom: 0.25rem; bottom: 0.25rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-end;
justify-content: flex-start; justify-content: flex-start;
gap: 0.125rem; gap: 0.125rem;
z-index: 2; z-index: 2;
} }
.prompt-nav-top-row {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.125rem;
}
.prompt-expand-button, .prompt-expand-button,
.prompt-history-button { .prompt-history-button {
@apply w-7 h-7 flex items-center justify-center rounded-md; @apply w-7 h-7 flex items-center justify-center rounded-md;
@@ -200,6 +208,18 @@
color: var(--button-danger-text, var(--text-inverted, #ffffff)); color: var(--button-danger-text, var(--text-inverted, #ffffff));
} }
.prompt-nav-voice-button {
min-width: 1.75rem;
width: 1.75rem;
height: 1.75rem;
border-radius: 0.375rem;
}
.prompt-nav-voice-button.is-recording {
min-width: 3.5rem;
width: auto;
}
.prompt-voice-button:disabled { .prompt-voice-button:disabled {
@apply opacity-50 cursor-not-allowed; @apply opacity-50 cursor-not-allowed;
} }
@@ -377,7 +397,7 @@
.prompt-input { .prompt-input {
min-height: 0; min-height: 0;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
padding-inline-end: 2.5rem; padding-inline-end: 5.5rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
} }