style(chat): restore composer active control states

This commit is contained in:
wasimysaid 2026-05-31 03:56:07 +02:00
commit b24a450f26
3 changed files with 75 additions and 134 deletions

View file

@ -699,8 +699,12 @@ const Composer: FC<{
{...inputProps}
/>
<ComposerRightControls
disabled={disabled || isComposing || hasPendingAttachments}
showSend={hasSendableContent}
disabled={
disabled ||
!hasSendableContent ||
isComposing ||
hasPendingAttachments
}
shouldBlockSend={shouldBlockSend}
menuSide={effectiveMenuSide}
/>
@ -1053,18 +1057,6 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
const activeLook = isEffort
? reasoningLockedOn || (effectiveReasoningVisualEnabled && !disabled)
: reasoningLockedOn || (effectiveReasoningEnabled && !disabled);
const canDismissReasoning =
activeLook &&
!disabled &&
!reasoningLockedOn &&
effectiveSupportsReasoningOff;
const dismissReasoning = () => {
if (!canDismissReasoning) return;
setReasoningEnabled(false);
applyQwenThinkingParams(false);
setPreserveThinking(false);
};
if (useDropdown) {
return (
<DropdownMenu>
@ -1074,7 +1066,6 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
disabled={disabled}
className="unsloth-thinking-pill"
data-active={activeLook ? "true" : "false"}
data-dismissible={canDismissReasoning ? "true" : undefined}
aria-label={thinkEffortAriaLabel({
modelLoaded,
reasoningDisabled: disabled,
@ -1086,21 +1077,6 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
<span>{isEffort ? `Thinking · ${effortLabel}` : "Thinking"}</span>
) : null}
<ArrowDownStandardIcon className="unsloth-thinking-chevron size-[15px]" />
{canDismissReasoning ? (
<XIcon
className="composer-pill-close unsloth-thinking-close"
aria-hidden={true}
onPointerDown={(event) => {
event.preventDefault();
event.stopPropagation();
dismissReasoning();
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
}}
/>
) : null}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@ -1233,7 +1209,6 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
}}
className="unsloth-thinking-pill"
data-active={activeLook ? "true" : "false"}
data-dismissible={canDismissReasoning ? "true" : undefined}
aria-label={thinkToggleAriaLabel({
reasoningLockedOn,
modelLoaded,
@ -1243,9 +1218,6 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
>
<ThinkIcon />
{activeLook ? <span>Thinking</span> : null}
{canDismissReasoning ? (
<XIcon className="composer-pill-close" aria-hidden={true} />
) : null}
</button>
);
};
@ -1727,10 +1699,9 @@ const ComposerToolsMenu: FC<{
const ComposerRightControls: FC<{
disabled?: boolean;
showSend?: boolean;
shouldBlockSend?: () => boolean;
menuSide?: "top" | "bottom";
}> = ({ disabled, showSend, shouldBlockSend, menuSide }) => {
}> = ({ disabled, shouldBlockSend, menuSide }) => {
return (
<div className="aui-composer-action-wrapper flex shrink-0 items-center gap-0.5">
<ReasoningToggle side={menuSide} />
@ -1758,29 +1729,27 @@ const ComposerRightControls: FC<{
</TooltipIconButton>
</ComposerPrimitive.StopDictation>
</ComposerPrimitive.If>
{showSend ? (
<AuiIf condition={({ thread }) => !thread.isRunning}>
<ComposerPrimitive.Send asChild={true}>
<TooltipIconButton
tooltip="Send message"
side="bottom"
type="submit"
variant="default"
size="icon"
disabled={disabled}
onClick={(event) => {
if (shouldBlockSend?.()) {
event.preventDefault();
}
}}
className="aui-composer-send composer-send-enter size-8 rounded-full disabled:bg-transparent disabled:text-foreground/40 disabled:opacity-100 disabled:pointer-events-none"
aria-label="Send message"
>
<ArrowUpIcon className="aui-composer-send-icon size-[22px] stroke-2" />
</TooltipIconButton>
</ComposerPrimitive.Send>
</AuiIf>
) : null}
<AuiIf condition={({ thread }) => !thread.isRunning}>
<ComposerPrimitive.Send asChild={true}>
<TooltipIconButton
tooltip="Send message"
side="bottom"
type="submit"
variant="default"
size="icon"
disabled={disabled}
onClick={(event) => {
if (shouldBlockSend?.()) {
event.preventDefault();
}
}}
className="aui-composer-send composer-send-enter size-8 rounded-full disabled:bg-primary/35 disabled:text-primary-foreground/80 disabled:opacity-100 disabled:pointer-events-none"
aria-label="Send message"
>
<ArrowUpIcon className="aui-composer-send-icon size-[22px] stroke-2" />
</TooltipIconButton>
</ComposerPrimitive.Send>
</AuiIf>
<AuiIf condition={({ thread }) => thread.isRunning}>
<ComposerPrimitive.Cancel asChild={true}>
<Button

View file

@ -555,17 +555,6 @@ export function SharedComposer({
? reasoningLockedOn ||
(effectiveReasoningVisualEnabled && !reasoningDisabled)
: reasoningLockedOn || (effectiveReasoningEnabled && !reasoningDisabled);
const canDismissReasoning =
thinkingActiveLook &&
!reasoningDisabled &&
!reasoningLockedOn &&
effectiveSupportsReasoningOff;
const dismissReasoning = () => {
if (!canDismissReasoning) return;
setReasoningEnabled(false);
applyQwenThinkingParams(false);
setPreserveThinking(false);
};
// Two-pill gating: Search pill lights up when the runtime has either
// a local tool runtime (supportsTools, gives us our Code/python + local
// web_search) OR a server-side web_search the provider runs for us
@ -1405,7 +1394,6 @@ export function SharedComposer({
disabled={reasoningDisabled}
className="unsloth-thinking-pill"
data-active={thinkingActiveLook ? "true" : "false"}
data-dismissible={canDismissReasoning ? "true" : undefined}
aria-label={thinkEffortAriaLabel({
modelLoaded,
reasoningDisabled,
@ -1424,21 +1412,6 @@ export function SharedComposer({
</span>
) : null}
<ArrowDownStandardIcon className="unsloth-thinking-chevron size-[15px]" />
{canDismissReasoning ? (
<XIcon
className="composer-pill-close unsloth-thinking-close"
aria-hidden={true}
onPointerDown={(event) => {
event.preventDefault();
event.stopPropagation();
dismissReasoning();
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
}}
/>
) : null}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@ -1576,7 +1549,6 @@ export function SharedComposer({
}}
className="unsloth-thinking-pill"
data-active={thinkingActiveLook ? "true" : "false"}
data-dismissible={canDismissReasoning ? "true" : undefined}
aria-label={thinkToggleAriaLabel({
reasoningLockedOn,
modelLoaded,
@ -1586,9 +1558,6 @@ export function SharedComposer({
>
<BulbIcon className="size-[15px]" />
{thinkingActiveLook ? <span>Thinking</span> : null}
{canDismissReasoning ? (
<XIcon className="composer-pill-close" aria-hidden={true} />
) : null}
</button>
)
) : null}
@ -1631,20 +1600,20 @@ export function SharedComposer({
>
<SquareIcon className="size-3 fill-current" />
</Button>
) : hasComposerContent ? (
) : (
<TooltipIconButton
tooltip="Send message"
side="bottom"
variant="default"
size="icon"
className="composer-send-enter size-8 rounded-full disabled:bg-transparent disabled:text-foreground/40 disabled:opacity-100 disabled:pointer-events-none"
className="composer-send-enter size-8 rounded-full disabled:bg-primary/35 disabled:text-primary-foreground/80 disabled:opacity-100 disabled:pointer-events-none"
onClick={send}
disabled={!canSend}
aria-label="Send message"
>
<ArrowUpIcon className="size-[22px] stroke-2" />
</TooltipIconButton>
) : null}
)}
</div>
</div>
</div>

View file

@ -900,19 +900,21 @@
}
.composer-pill-btn {
@apply flex h-6 cursor-pointer items-center gap-1 rounded-full pl-1 pr-2 py-0 text-[14px] font-medium leading-6 text-muted-foreground/75 transition-colors hover:bg-black/[0.06] dark:hover:bg-white/[0.08] disabled:cursor-not-allowed disabled:opacity-40;
@apply flex h-6 cursor-pointer items-center gap-1 rounded-full pl-1 pr-2 py-0 text-[14px] font-medium leading-6 text-muted-foreground/75 transition-colors hover:bg-primary/10 dark:hover:bg-primary/15 disabled:cursor-not-allowed disabled:opacity-40;
}
.composer-pill-btn:is(:hover, :focus-visible) {
background-color: color-mix(in oklch, var(--primary) 10%, transparent);
color: var(--foreground);
}
.dark .composer-pill-btn:is(:hover, :focus-visible) {
background-color: color-mix(in oklch, var(--primary) 15%, transparent);
color: var(--foreground);
}
.composer-pill-btn:is(:hover, :focus-visible),
.composer-pill-btn[data-active="true"] {
background-color: rgba(0, 0, 0, 0.08);
color: var(--foreground);
}
.dark .composer-pill-btn:is(:hover, :focus-visible),
.dark .composer-pill-btn[data-active="true"] {
background-color: rgba(255, 255, 255, 0.08);
color: var(--foreground);
color: var(--primary);
}
.composer-pill-btn > span {
@ -929,14 +931,15 @@
.composer-pill-close {
display: none;
order: -1;
}
.composer-pill-btn[data-active="true"]:not(:disabled):is(
:hover,
:focus-visible
) {
background-color: rgba(0, 0, 0, 0.14);
padding-right: 0.25rem;
background-color: color-mix(in oklch, var(--primary) 10%, transparent);
color: var(--primary);
}
.dark
@ -944,7 +947,16 @@
:hover,
:focus-visible
) {
background-color: rgba(255, 255, 255, 0.14);
background-color: color-mix(in oklch, var(--primary) 15%, transparent);
color: var(--primary);
}
.composer-pill-btn[data-active="true"]:not(:disabled):is(
:hover,
:focus-visible
)
> svg:not(.composer-pill-close) {
display: none;
}
.composer-pill-btn[data-active="true"]:not(:disabled):is(
@ -1106,7 +1118,7 @@
/* Right-side Thinking pill (toggle or dropdown). */
.unsloth-thinking-pill {
@apply inline-flex h-6 shrink-0 cursor-pointer items-center gap-1 rounded-full pl-1 pr-2 py-0 text-[14px] font-medium leading-6 text-muted-foreground/75 transition-colors hover:bg-black/[0.06] dark:hover:bg-white/[0.08] disabled:cursor-not-allowed disabled:opacity-40;
@apply inline-flex h-6 shrink-0 cursor-pointer items-center gap-1 rounded-full pl-1 pr-2 py-0 text-[14px] font-medium leading-6 text-muted-foreground/75 transition-colors hover:bg-primary/10 dark:hover:bg-primary/15 disabled:cursor-not-allowed disabled:opacity-40;
}
.unsloth-thinking-pill > span {
@ -1121,54 +1133,45 @@
flex-shrink: 0;
}
.unsloth-thinking-pill[data-active="true"],
.unsloth-thinking-pill[data-state="open"] {
background-color: rgba(0, 0, 0, 0.08);
color: var(--foreground);
.unsloth-thinking-pill[data-active="true"] {
color: var(--primary);
/* See .unsloth-composer-plus: re-enable pointer-events on the open trigger. */
pointer-events: auto;
cursor: pointer;
}
.dark .unsloth-thinking-pill[data-active="true"],
.unsloth-thinking-pill[data-state="open"] {
background-color: rgba(0, 0, 0, 0.08);
color: var(--foreground);
pointer-events: auto;
cursor: pointer;
}
.unsloth-thinking-pill[data-active="true"][data-state="open"] {
color: var(--primary);
}
.dark .unsloth-thinking-pill[data-state="open"] {
background-color: rgba(255, 255, 255, 0.08);
color: var(--foreground);
}
.unsloth-thinking-pill .composer-pill-close {
display: none;
width: 16px !important;
height: 16px !important;
flex-shrink: 0;
.dark .unsloth-thinking-pill[data-active="true"][data-state="open"] {
color: var(--primary);
}
.unsloth-thinking-pill[data-dismissible="true"][data-active="true"]:not(
:disabled
):is(:hover, :focus-visible) {
background-color: rgba(0, 0, 0, 0.14);
padding-right: 0.25rem;
.unsloth-thinking-pill:is(:hover, :focus-visible) {
background-color: color-mix(in oklch, var(--primary) 10%, transparent);
color: var(--foreground);
}
.dark
.unsloth-thinking-pill[data-dismissible="true"][data-active="true"]:not(
:disabled
):is(:hover, :focus-visible) {
background-color: rgba(255, 255, 255, 0.14);
.dark .unsloth-thinking-pill:is(:hover, :focus-visible) {
background-color: color-mix(in oklch, var(--primary) 15%, transparent);
color: var(--foreground);
}
.unsloth-thinking-pill[data-dismissible="true"][data-active="true"]:not(
:disabled
):is(:hover, :focus-visible)
.composer-pill-close {
display: inline-flex;
}
.unsloth-thinking-pill[data-dismissible="true"][data-active="true"]:not(
:disabled
):is(:hover, :focus-visible)
.unsloth-thinking-chevron {
display: none;
.unsloth-thinking-pill[data-active="true"]:is(:hover, :focus-visible) {
color: var(--primary);
}
/* Smaller tick for selected Thinking options. */