Compact thinking control in narrow composers (#7150)

* Compact thinking control in narrow composers

* Use UTF-8 in responsive layout tests
This commit is contained in:
Michael Han 2026-07-15 09:58:18 -07:00 committed by GitHub
commit c5ae208abb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 73 additions and 6 deletions

View file

@ -2270,6 +2270,7 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
type="button"
disabled={disabled}
className="unsloth-thinking-pill"
data-pill-label="Thinking settings"
data-active={activeLook ? "true" : "false"}
aria-label={thinkEffortAriaLabel({
modelLoaded,
@ -2279,9 +2280,11 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
>
<ThinkIcon />
{activeLook ? (
<span>{isEffort ? `Thinking · ${effortLabel}` : "Thinking"}</span>
<span className="unsloth-thinking-label">
{isEffort ? `Thinking · ${effortLabel}` : "Thinking"}
</span>
) : null}
<ArrowDownStandardIcon className="size-[15px]" />
<ArrowDownStandardIcon className="unsloth-thinking-caret size-[15px]" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@ -2421,6 +2424,7 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
}
}}
className="unsloth-thinking-pill"
data-pill-label="Thinking"
data-active={activeLook ? "true" : "false"}
aria-label={thinkToggleAriaLabel({
reasoningLockedOn,
@ -2432,7 +2436,9 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
<PillGlyph>
<ThinkIcon />
</PillGlyph>
{activeLook ? <span>Thinking</span> : null}
{activeLook ? (
<span className="unsloth-thinking-label">Thinking</span>
) : null}
</button>
);
};

View file

@ -1890,6 +1890,7 @@ export function SharedComposer({
type="button"
disabled={reasoningDisabled}
className="unsloth-thinking-pill"
data-pill-label="Thinking settings"
data-active={thinkingActiveLook ? "true" : "false"}
aria-label={thinkEffortAriaLabel({
modelLoaded,
@ -1899,7 +1900,7 @@ export function SharedComposer({
>
<BulbIcon className="size-[15.5px]" />
{thinkingActiveLook ? (
<span>
<span className="unsloth-thinking-label">
{isEffort
? `Thinking · ${formatReasoningEffortLabel(
reasoningEffort,
@ -1908,7 +1909,7 @@ export function SharedComposer({
: "Thinking"}
</span>
) : null}
<ArrowDownStandardIcon className="size-[15px]" />
<ArrowDownStandardIcon className="unsloth-thinking-caret size-[15px]" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@ -2055,6 +2056,7 @@ export function SharedComposer({
}
}}
className="unsloth-thinking-pill"
data-pill-label="Thinking"
data-active={thinkingActiveLook ? "true" : "false"}
aria-label={thinkToggleAriaLabel({
reasoningLockedOn,
@ -2066,7 +2068,9 @@ export function SharedComposer({
<PillGlyph>
<BulbIcon className="size-[15.5px]" />
</PillGlyph>
{thinkingActiveLook ? <span>Thinking</span> : null}
{thinkingActiveLook ? (
<span className="unsloth-thinking-label">Thinking</span>
) : null}
</button>
)
) : null}

View file

@ -1409,6 +1409,7 @@ html[data-chat-font] .aui-root {
.chat-composer-surface {
@apply relative flex w-full flex-col rounded-[32px] bg-background dark:bg-card px-1 pt-2 outline-none transition-shadow;
container-type: inline-size;
font-family: var(--font-sans);
background-clip: padding-box;
background-color: #ffffff;
@ -1569,6 +1570,7 @@ html[data-chat-font] .aui-root {
/* Pill composer; own classes so compare-mode keeps its stacked layout. */
.unsloth-composer-surface {
@apply relative flex w-full flex-col rounded-[32px] bg-background dark:bg-card px-3 py-3 outline-none transition-shadow;
container-type: inline-size;
font-family: var(--font-sans);
background-clip: padding-box;
background-color: #ffffff;
@ -1744,6 +1746,28 @@ html[data-chat-font] .aui-root {
cursor: pointer;
}
/* Keep Thinking on the control row in narrow split layouts. */
@container (max-width: 36rem) {
.unsloth-thinking-pill {
@apply size-8 justify-center gap-0 px-0;
min-height: 2rem;
}
.unsloth-thinking-label,
.unsloth-thinking-caret {
display: none;
}
.unsloth-thinking-pill[data-pill-label] {
position: relative;
}
.unsloth-thinking-pill[data-pill-label]:not([data-state="open"]):hover::after {
content: attr(data-pill-label);
@apply pointer-events-none absolute bottom-[calc(100%+6px)] left-1/2 z-50 -translate-x-1/2 rounded-full bg-black px-2.5 py-1.5 text-[11px] font-medium leading-snug whitespace-nowrap text-white shadow-md;
}
}
/* Smaller tick for selected Thinking options. */
.unsloth-tick {
width: 0.8rem !important;

View file

@ -0,0 +1,33 @@
"""Responsive contract for the composer Thinking control."""
from pathlib import Path
REPO = Path(__file__).resolve().parents[2]
THREAD_TSX = REPO / "studio/frontend/src/components/assistant-ui/thread.tsx"
SHARED_TSX = REPO / "studio/frontend/src/features/chat/shared-composer.tsx"
INDEX_CSS = REPO / "studio/frontend/src/index.css"
def test_thinking_control_has_compact_hooks_in_both_composers():
for path in (THREAD_TSX, SHARED_TSX):
source = path.read_text(encoding = "utf-8")
assert 'className="unsloth-thinking-label"' in source
assert "unsloth-thinking-caret size-[15px]" in source
assert 'data-pill-label="Thinking settings"' in source
def test_narrow_composer_collapses_thinking_to_the_bulb():
css = INDEX_CSS.read_text(encoding = "utf-8")
# Query the composer width instead of the full viewport.
assert css.count("container-type: inline-size;") >= 2
compact_start = css.index("@container (max-width: 36rem)")
compact_end = css.index("/* Smaller tick", compact_start)
compact_rule = css[compact_start:compact_end]
assert ".unsloth-thinking-pill" in compact_rule
assert "@apply size-8" in compact_rule
assert ".unsloth-thinking-label" in compact_rule
assert ".unsloth-thinking-caret" in compact_rule
assert "display: none;" in compact_rule