fix(ui): unify thinking controls with icon buttons

This commit is contained in:
Shantur Rathore
2026-02-09 16:20:33 +00:00
parent d143faf8eb
commit 01300a81de
3 changed files with 73 additions and 57 deletions

View File

@@ -1,5 +1,5 @@
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, untrack } from "solid-js"
import { ExternalLink, FoldVertical, Trash2 } from "lucide-solid"
import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, Trash2 } from "lucide-solid"
import MessageItem from "./message-item"
import ToolCall from "./tool-call"
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
@@ -1010,10 +1010,13 @@ function ReasoningCard(props: ReasoningCardProps) {
const toggle = () => setExpanded((prev) => !prev)
const viewHideLabel = () =>
expanded() ? t("messageBlock.reasoning.indicator.hide") : t("messageBlock.reasoning.indicator.view")
const hasDeleteTarget = () => Boolean(props.partId)
const canDelete = () => hasDeleteTarget() && !deleting()
const handleDelete = async (event: Event) => {
const handleDelete = async (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
if (!canDelete()) return
@@ -1033,56 +1036,66 @@ function ReasoningCard(props: ReasoningCardProps) {
return (
<div class="message-reasoning-card">
<button
type="button"
class="message-reasoning-toggle"
onClick={toggle}
aria-expanded={expanded()}
aria-label={expanded() ? t("messageBlock.reasoning.collapseAriaLabel") : t("messageBlock.reasoning.expandAriaLabel")}
>
<span class="message-reasoning-label flex flex-wrap items-center gap-2">
<span>{t("messageBlock.reasoning.thinkingLabel")}</span>
<Show when={props.showAgentMeta && (agentIdentifier() || modelIdentifier())}>
<span class="message-step-meta-inline">
<Show when={agentIdentifier()}>
{(value) => (
<span class="font-medium text-[var(--message-assistant-border)]">{t("messageBlock.step.agentLabel", { agent: value() })}</span>
)}
</Show>
<Show when={modelIdentifier()}>
{(value) => (
<span class="font-medium text-[var(--message-assistant-border)]">{t("messageBlock.step.modelLabel", { model: value() })}</span>
)}
</Show>
</span>
</Show>
</span>
<span class="message-reasoning-meta">
<span class="message-reasoning-indicator">
{expanded() ? t("messageBlock.reasoning.indicator.hide") : t("messageBlock.reasoning.indicator.view")}
<div class="message-reasoning-header">
<button
type="button"
class="message-reasoning-toggle"
onClick={toggle}
aria-expanded={expanded()}
aria-label={expanded() ? t("messageBlock.reasoning.collapseAriaLabel") : t("messageBlock.reasoning.expandAriaLabel")}
>
<span class="message-reasoning-label flex flex-wrap items-center gap-2">
<span>{t("messageBlock.reasoning.thinkingLabel")}</span>
<Show when={props.showAgentMeta && (agentIdentifier() || modelIdentifier())}>
<span class="message-step-meta-inline">
<Show when={agentIdentifier()}>
{(value) => (
<span class="font-medium text-[var(--message-assistant-border)]">{t("messageBlock.step.agentLabel", { agent: value() })}</span>
)}
</Show>
<Show when={modelIdentifier()}>
{(value) => (
<span class="font-medium text-[var(--message-assistant-border)]">{t("messageBlock.step.modelLabel", { model: value() })}</span>
)}
</Show>
</span>
</Show>
</span>
</button>
<div class="message-reasoning-actions">
<button
type="button"
class="message-action-button"
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
toggle()
}}
aria-label={viewHideLabel()}
title={viewHideLabel()}
>
<Show when={expanded()} fallback={<ChevronsUpDown class="w-3.5 h-3.5" aria-hidden="true" />}>
<ChevronsDownUp class="w-3.5 h-3.5" aria-hidden="true" />
</Show>
</button>
<Show when={hasDeleteTarget()}>
<span
class={`message-reasoning-indicator${canDelete() ? "" : " opacity-50 pointer-events-none"}`}
role="button"
tabIndex={0}
<button
type="button"
class="message-action-button"
onClick={handleDelete}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
handleDelete(event)
}
}}
disabled={!canDelete()}
aria-label={t("messagePart.actions.deleteTitle")}
title={t("messagePart.actions.deleteTitle")}
>
{deleting() ? t("messagePart.actions.deleting") : t("messagePart.actions.delete")}
</span>
<Trash2 class="w-3.5 h-3.5" aria-hidden="true" />
</button>
</Show>
<span class="message-reasoning-time">{timestamp()}</span>
</span>
</button>
</div>
</div>
<Show when={expanded()}>
<div class="message-reasoning-expanded">

View File

@@ -305,19 +305,6 @@ export default function MessageItem(props: MessageItemProps) {
>
<Copy class="w-3.5 h-3.5" aria-hidden="true" />
</button>
<Show when={deletableTextPartId()}>
{(partId) => (
<button
class="message-action-button"
onClick={() => void handleDeletePart(partId())}
disabled={isDeletingPart(partId())}
title={isDeletingPart(partId()) ? t("messagePart.actions.deleting") : t("messagePart.actions.delete")}
aria-label={isDeletingPart(partId()) ? t("messagePart.actions.deleting") : t("messagePart.actions.delete")}
>
<Trash2 class="w-3.5 h-3.5" aria-hidden="true" />
</button>
)}
</Show>
</div>
</Show>
<Show when={!isUser()}>

View File

@@ -298,11 +298,20 @@
gap: 0;
}
.message-reasoning-header {
display: flex;
align-items: stretch;
justify-content: space-between;
gap: 0.5rem;
}
.message-reasoning-toggle {
width: 100%;
flex: 1 1 auto;
min-width: 0;
width: auto;
display: flex;
align-items: center;
justify-content: space-between;
justify-content: flex-start;
gap: 0.65rem;
background: none;
border: none;
@@ -314,6 +323,13 @@
transition: background-color 0.2s ease, box-shadow 0.2s ease;
}
.message-reasoning-actions {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.25rem 0.6rem 0.25rem 0;
}
.message-reasoning-toggle:hover {
background-color: var(--surface-hover);
}