diff --git a/studio/frontend/src/components/assistant-ui/thread.tsx b/studio/frontend/src/components/assistant-ui/thread.tsx index b3396d9a00..ad5016aae1 100644 --- a/studio/frontend/src/components/assistant-ui/thread.tsx +++ b/studio/frontend/src/components/assistant-ui/thread.tsx @@ -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" }> = ({ > {activeLook ? ( - {isEffort ? `Thinking · ${effortLabel}` : "Thinking"} + + {isEffort ? `Thinking · ${effortLabel}` : "Thinking"} + ) : null} - + = ({ } }} 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" }> = ({ - {activeLook ? Thinking : null} + {activeLook ? ( + Thinking + ) : null} ); }; diff --git a/studio/frontend/src/features/chat/shared-composer.tsx b/studio/frontend/src/features/chat/shared-composer.tsx index babff892bb..da0285ef49 100644 --- a/studio/frontend/src/features/chat/shared-composer.tsx +++ b/studio/frontend/src/features/chat/shared-composer.tsx @@ -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({ > {thinkingActiveLook ? ( - + {isEffort ? `Thinking · ${formatReasoningEffortLabel( reasoningEffort, @@ -1908,7 +1909,7 @@ export function SharedComposer({ : "Thinking"} ) : null} - + - {thinkingActiveLook ? Thinking : null} + {thinkingActiveLook ? ( + Thinking + ) : null} ) ) : null} diff --git a/studio/frontend/src/index.css b/studio/frontend/src/index.css index bcc3f6f9d3..159eba743f 100644 --- a/studio/frontend/src/index.css +++ b/studio/frontend/src/index.css @@ -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; diff --git a/tests/studio/test_chat_thinking_compact_layout.py b/tests/studio/test_chat_thinking_compact_layout.py new file mode 100644 index 0000000000..23b6797782 --- /dev/null +++ b/tests/studio/test_chat_thinking_compact_layout.py @@ -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