studio: redesign chat composer by shimmyshimmer · Pull Request #5891 · unslothai/unsloth · GitHub
Skip to content

studio: redesign chat composer - #5891

Merged
danielhanchen merged 101 commits into
mainfrom
studio-composer-clean
Jun 3, 2026
Merged

studio: redesign chat composer#5891
danielhanchen merged 101 commits into
mainfrom
studio-composer-clean

Conversation

@shimmyshimmer

@shimmyshimmer shimmyshimmer commented May 31, 2026

Copy link
Copy Markdown
Member

Summary

Reworks the Studio chat composer (the new-chat welcome screen and the compare composer) into a single rounded pill surface with a softer, lighter look, wires the plus menu up to the real Projects and MCP features now that both have landed on main, and then carries that same visual language across the rest of Studio: consistent menus and overlays, matching light and dark corner radii, a shared elevation, unified icons, and a hand cursor on every clickable control.

  • The empty composer is a single centered pill. Once you start typing, or turn on a tool, it expands to two rows with the text on top and the controls below. The textarea is never remounted, so focus and input are kept.
  • The plus button opens a rounded dropdown (Add photos and files, Recent audio, Web search, Code, Canvas, MCP, RAG, Compare chat, and a Projects submenu).
  • Search and Code pills sit next to the plus button. Canvas and MCP are opt-in pills that only appear once toggled on in the plus menu, the same way Canvas works.
  • Thinking sits on the right as a toggle, or a dropdown with effort levels and preserve thinking for models that support them.
  • The welcome screen shows a time-of-day sloth mascot next to the heading, the heading sits at a screen-height relative offset, and the input placeholder is "Ask anything".

MCP

  • Integrates the MCP work from main. The MCP pill opens a dropdown of keyless presets (Context7, Exa, Hugging Face) plus any custom servers and an Add custom MCP entry.
  • MCP is opt-in from the plus menu. Turning it on expands the composer and shows the pill; the pill has a chevron so it reads as openable, and drops the chevron when the pills collapse so the icon is not squished.
  • The dropdown shares the plus menu look. Because the MCP menu is a little wider, its corner radius is nudged up slightly so it reads as round as the narrower plus menu.

Projects

  • The Projects submenu lists the most recent projects and a New project entry. New project opens a small create dialog and jumps straight into the new project instead of routing to the projects list.
  • The chat context menu (the per-chat three-dot menu) matches the plus menu styling. Its submenu is labelled "Move to project" and uses the folder-export icon. The menu opens down and to the right of the trigger and is sized so the label fits on one line.
  • Compare now lives in the plus menu, so it has been removed from the sidebar. The collapsed sidebar shows a Train icon alongside New chat, Search and Projects.

Menus, dropdowns and overlays

  • Every overlay (dropdown menu, submenu, select, combobox, popover) is borderless. In light mode they use the chat box shadow; in dark mode they keep a background-tinted shadow so a menu does not merge into a same-color surface underneath it.
  • Dark mode corner radius now matches light mode everywhere through a single radius token, so boxes are consistently rounded across themes.
  • The message export menu, the search dialog, the model selector and the recipes New Recipe menu all follow the same borderless, shared-shadow treatment.
  • The model selector gets roomier padding, a borderless box, rounder hover rows, and a more visible hover color in dark mode.

Settings

  • Adds a Settings title above the tabs. The tabs are a rounded pill hover with a small left inset, and the vertical divider only shows in light mode.
  • Drops the outline ring, removes the dark-mode shadow, gives the close button a circular hover, and makes the profile and account menus larger and more rounded with more padding.
  • The avatar camera control uses the Hugeicons camera-01 glyph and the chat box shadow.

Fine-tuning Studio (Train) page

  • The active tab is a borderless pill instead of a bottom underline, with roomier padding, more space between tabs, and more clearance above the first card.

Icons and cursors

  • All download glyphs (export as markdown, the Downloaded label, audio, image and artifact download buttons, and the chat settings export) use the Hugeicons download-01 icon.
  • App-wide: every clickable control shows the hand cursor. Disabled controls (native disabled, aria-disabled, and Radix data-disabled) keep the default cursor.

Sidebars and run settings

  • Both sidebars only show their scrollbar while hovered.
  • The run settings header sits outside the scroll area, so the close button keeps its position whether or not the scrollbar shows and lines up with the topbar open button. The header text is slightly larger.
  • The profile row in the sidebar footer has a taller, rounder hover area with tighter vertical padding, while the avatar and name stay centered.
  • The Train and Recents section labels are slightly smaller so they read more clearly as headers.
  • Nav row hover boxes and the account button are a touch more rounded.

Robustness

  • Hardens the projects and threads list calls so they always return an array. An older or misbehaving backend that omits the field or sends a non-array can no longer leave the cache undefined and white-screen the chat page.

Notes

  • The new styles are scoped to their own unsloth- prefixed classes, so compare mode (SharedComposer) keeps its own stacked layout.
  • Canvas and RAG remain UI surfaces ahead of their full features; Web search, Code, Compare, MCP and Projects are wired to real behavior.

Test plan

  • Empty composer renders as a single centered pill
  • Typing, or toggling a tool, switches to the two row layout and keeps focus
  • Plus menu opens on the correct side with the new entries
  • Toggling MCP shows the pill and the presets dropdown for tool-capable models
  • New project creates and opens the project
  • Chat context menu matches the plus menu and "Move to project" reads on one line
  • Every dropdown, select and popover is borderless with the right shadow in light and dark
  • Train page active tab shows the pill, not the underline
  • Clickable controls show the hand cursor, disabled controls do not
  • Light and dark mode look correct

Unsloth and others added 8 commits May 29, 2026 01:40
Reworks the new-chat composer and the compare composer into a single
rounded pill surface with a softer, lighter look.

- New welcome screen with a time-of-day sloth mascot and a lighter
  heading.
- One rounded composer surface with a soft drop shadow. The input grows
  inline as you type and collapses back to a single row when cleared.
- Tools and attachments live in a single plus menu; the thinking control
  is a compact pill with a reasoning-effort submenu.
- Inlined glyphs for the thinking, send, and dictate controls, kept in
  sync across the main and compare composers.
- Toast notifications match the composer surface: no border line, the
  same drop shadow, and the same dark surface color, with a ring-less
  close button.
- Dark mode: the side-menu shadow blends into the background, hovered
  menu rows read clearly, and their roundness matches light mode.
- Composer styles use dedicated unsloth- prefixed classes so compare
  mode keeps its own stacked layout.
- Compare composer: keep "Preserve thinking" consistent with reasoning,
  matching the main composer. Enabling it now turns reasoning on, and
  disabling reasoning (the None option or the Thinking toggle) turns it
  off, so the invalid "preserve on while thinking off" state can't occur.
- Guard crypto.randomUUID in the Compare action. It is undefined in
  non-secure contexts (HTTP over a LAN IP) and would throw; fall back to
  a timestamped random id, matching createNavigationNonce.
The Search and Code pills only lit up when the tool was usable right now
(a model loaded and capable), so a tool turned on from the + menu showed
as off in the pill while the menu showed it on. toolsEnabled is persisted
and takes effect once a capable model loads, so the pill should reflect it.
The pills now disable only when a loaded model lacks the capability, and
otherwise reflect the selected state. Applied to the main and compare
composers.
Make the "MCP Servers" heading in the chat Configuration sheet link to the
MCP PR, keeping the chevron as the toggle. The label and chevron are rendered
as siblings so we don't nest an <a> inside a <button>.

Also add cursor-pointer to the composer pills and the thinking pill so hovering
a clickable pill shows the hand cursor instead of the default arrow.
- Composer expands to two rows only once the input wraps to a second line,
  not on the first keystroke. Re-measure the autosize textarea on the width
  swap so expanding no longer leaves a stray blank row.
- Light-mode composer shadow now matches Gemini's soft elevation.
- Plus menu: replace Canvas with a More submenu (Canvas, Compare chat, RAG)
  and add Code above MCP. Active Web search/Code items use medium weight.
- Compare mode: the plus side menu, Search/Code toggles, and a Compare exit
  pill now match single chat, with the thinking control on the right.
- Projects menu entries link to their tracking PR (#5725).
- Add cursor-pointer to the composer plus button.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the chat composer UI to introduce a unified, two-row layout with a consolidated tools and attachments plus-menu, alongside an updated reasoning/thinking toggle that supports effort levels and preserve-thinking options across both single-chat and compare-chat composers. The review feedback highlights a performance optimization to prevent layout thrashing during multiline text measurement, a bug where disabling tools for Kimi models permanently overwrites user preferences due to a missing { persist: false } option, and accessibility concerns where preventing auto-focus on menu close breaks keyboard navigation.

Comment on lines +463 to +480

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling getComputedStyle inside a useEffect that triggers on every composerText change causes layout thrashing (forced synchronous layout) on every single keystroke. This can severely degrade typing performance and cause input lag.

To optimize this, cache the measured lineHeight and padding values in a useRef so that getComputedStyle is only called once when the element is first measured.

  const inputRef = useRef<HTMLTextAreaElement>(null);
  const measurementsRef = useRef<{ lineHeight: number; padding: number } | null>(null);
  const [isMultiline, setIsMultiline] = useState(false);
  useEffect(() => {
    if (composerText.length === 0) {
      setIsMultiline(false);
      return;
    }
    const el = inputRef.current;
    if (!el) {
      return;
    }
    if (!measurementsRef.current) {
      const cs = getComputedStyle(el);
      const lineHeight = Number.parseFloat(cs.lineHeight) || 24;
      const padTop = Number.parseFloat(cs.paddingTop) || 0;
      const padBottom = Number.parseFloat(cs.paddingBottom) || 0;
      measurementsRef.current = { lineHeight, padding: padTop + padBottom };
    }
    const { lineHeight, padding } = measurementsRef.current;
    const contentHeight = el.scrollHeight - padding;
    setIsMultiline((prev) => prev || contentHeight > lineHeight * 1.5);
  }, [composerText]);

Comment on lines +1065 to +1067
if (isKimiExternal && toolsEnabled) {
setToolsEnabled(false);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When disabling tools for Kimi models due to mutual exclusion with thinking, calling setToolsEnabled(false) without { persist: false } will permanently overwrite the user's persisted search preference. This differs from the correct implementation in shared-composer.tsx which uses { persist: false } to avoid mutating global preferences.

Suggested change
if (isKimiExternal && toolsEnabled) {
setToolsEnabled(false);
}
if (isKimiExternal && toolsEnabled) {
setToolsEnabled(false, { persist: false });
}

Comment on lines +1093 to 1095
if (isKimiExternal && next && toolsEnabled) {
setToolsEnabled(false);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When disabling tools for Kimi models due to mutual exclusion with thinking, calling setToolsEnabled(false) without { persist: false } will permanently overwrite the user's persisted search preference. Pass { persist: false } to preserve the user's global settings.

Suggested change
if (isKimiExternal && next && toolsEnabled) {
setToolsEnabled(false);
}
if (isKimiExternal && next && toolsEnabled) {
setToolsEnabled(false, { persist: false });
}

Comment on lines 1152 to 1154
if (isKimiExternal && next && toolsEnabled) {
setToolsEnabled(false);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When disabling tools for Kimi models due to mutual exclusion with thinking, calling setToolsEnabled(false) without { persist: false } will permanently overwrite the user's persisted search preference. Pass { persist: false } to preserve the user's global settings.

Suggested change
if (isKimiExternal && next && toolsEnabled) {
setToolsEnabled(false);
}
if (isKimiExternal && next && toolsEnabled) {
setToolsEnabled(false, { persist: false });
}

Comment on lines +1408 to +1411
className="unsloth-plus-menu w-[212px]"
// Don't refocus the + on close; the restored focus showed a stray
// focus-visible ring.
onCloseAutoFocus={(event) => event.preventDefault()}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using onCloseAutoFocus={(event) => event.preventDefault()} on the DropdownMenuContent prevents the trigger button from regaining focus when the menu is closed. This breaks keyboard navigation and accessibility (WCAG 2.1), as keyboard users will lose their focus position.

Instead of dropping focus, remove onCloseAutoFocus and handle any unwanted focus ring styles via CSS :focus-visible on the trigger button.

Suggested change
className="unsloth-plus-menu w-[212px]"
// Don't refocus the + on close; the restored focus showed a stray
// focus-visible ring.
onCloseAutoFocus={(event) => event.preventDefault()}
className="unsloth-plus-menu w-[212px]"

Comment on lines +1025 to +1026
className="unsloth-plus-menu w-[212px]"
onCloseAutoFocus={(event) => event.preventDefault()}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using onCloseAutoFocus={(event) => event.preventDefault()} on the DropdownMenuContent breaks keyboard navigation and accessibility (WCAG 2.1) by dropping focus when the menu is closed.

Instead of dropping focus, remove onCloseAutoFocus and handle any unwanted focus ring styles via CSS :focus-visible on the trigger button.

Suggested change
className="unsloth-plus-menu w-[212px]"
onCloseAutoFocus={(event) => event.preventDefault()}
className="unsloth-plus-menu w-[212px]"

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d53d49f22

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

>
<HeadphonesIcon className="size-4.5 stroke-[1.5px]" />
</TooltipIconButton>
<DropdownMenuItem onSelect={() => audioInputRef.current?.click()}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the audio input mounted while picking files

For audio-capable models, selecting this item closes the Radix dropdown immediately after onSelect, which unmounts the hidden <input> because it is rendered inside the menu content. The OS file picker then returns to a detached/unmounted input, so the onChange handler is not reliably delivered and the selected audio is lost; keep the input outside the dropdown content or prevent the menu from closing until after selection.

Useful? React with 👍 / 👎.

typeof globalThis.crypto?.randomUUID === "function"
? globalThis.crypto.randomUUID()
: `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
navigate({ to: "/chat", search: { compare: compareId } });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the current chat before opening compare

When compare is opened from this new composer menu while viewing an existing thread, this path clears activeThreadId and navigates to compare without going through ChatPage.enterCompare, which is the path that saves viewBeforeCompareRef. Pressing the new Compare pill to exit then sees no saved view and falls back to /chat, so users lose their place instead of returning to the thread they started from.

Useful? React with 👍 / 👎.

- Active tool pills show an x on hover to signal click-to-disable
- Plus button rotates into an x when the tools menu opens
- Composer surface uses a 32px radius and a taller single-line height
- Even, ChatGPT-style spacing between the plus and tool pills in both the single and compare composers
- Send and mic circles resized and spaced, with the arrow centered in the circle
- Chat search box gets a borderless, soft Gemini-style shadow
@shimmyshimmer

Copy link
Copy Markdown
Member Author

Pushed a round of composer polish on top of the redesign.

Tool pills

  • Active pills (Search, Code, Compare) swap their icon for an x on hover, so it is clear that clicking turns the tool off.
  • Even, ChatGPT-style spacing between the plus button and the pills, in both the single and compare composers.
  • Bumped the tool and Thinking icon sizes by 1px for consistency.

Plus menu

  • Opening the plus menu rotates the plus into an x with a smooth ease-in-out, and it keeps animating under reduced motion.

Composer surface

  • 32px corner radius and a taller single-line height, so it reads fully round on one line and degrades cleanly to a rounded rectangle when the text wraps.

Send and mic

  • Resized the send and mic circles, added spacing between them, and centered the arrow in the circle.

Chat search

  • Borderless, soft Gemini-style shadow on the Search chats box.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4673957da7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1452 to +1455
<DropdownMenuItem
className={toolsEnabled ? "text-primary font-medium" : undefined}
onSelect={() => setToolsEnabled(!toolsEnabled)}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor Kimi search/thinking exclusion in the menu

When the selected external provider is Kimi and the user enables Web search from this new + menu, this handler only flips toolsEnabled; unlike the visible Search pill, it does not also disable reasoning. The backend Kimi web-search path forces thinking: disabled, so the UI can leave the Think pill active while all subsequent searched Kimi turns run without thinking, which is misleading and changes results for users who enabled search through the menu.

Useful? React with 👍 / 👎.

Cross-engine checks (Chromium, Firefox, WebKit) flagged the active-pill
hover x as a fixed 14px, so it popped smaller than the 19px Code icon.
Fill the glyph slot instead so the x tracks whatever icon it covers.
…e composer

The single-chat composer flipped the other control off when toggling
search or thinking on Kimi, but without { persist: false }, so it
overwrote the user's saved preference. Match shared-composer and keep
the side effect session-only.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40b424260e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 958 to 961
{
isReasoningProvider:
selectedExternalProvider?.isReasoningModel === true,
baseUrl: selectedExternalProvider?.baseUrl ?? null,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass baseUrl when resolving Gemini thinking

When the selected provider is Gemini with a custom OpenAI-compatible base URL, this call now omits baseUrl from getExternalReasoningCapabilities. That resolver uses options.baseUrl to detect custom Gemini gateways and hide the native Gemini thinking ladder; without it, the docked composer advertises/enables thinking controls that the backend cannot honor on the OpenAI-compatible /chat/completions path, so user-selected thinking settings are misleading or silently ignored. Pass selectedExternalProvider?.baseUrl here as the shared compare composer still does.

Useful? React with 👍 / 👎.

Add scoped marker classes so the model picker trigger and every
clickable element in its menu (tabs, model rows, delete, eject) show a
pointer cursor; disabled items stay not-allowed.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

Follow-up fixes pushed.

Tool pills

  • The active-pill hover x now fills the icon slot, so it matches the icon it replaces instead of popping smaller on the wider Code pill.

Kimi search and thinking

  • In the single-chat composer, toggling search or thinking on Kimi flips the other control off as a session-only change again. It was overwriting the saved preference because the side effect did not pass persist false. This now matches the compare composer.

Model selector

  • The picker trigger and every clickable item in its menu (tabs, model rows, delete, eject) show a pointer cursor on hover. Disabled items stay not-allowed and the search field keeps a text cursor.

Checked the composer and model selector styles render consistently across Chromium, Firefox and WebKit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1041e81d84

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

<DropdownMenuSeparator />
<DropdownMenuItem
className={toolsEnabled ? "text-primary font-medium" : undefined}
onSelect={() => setToolsEnabled(!toolsEnabled)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Kimi search and thinking mutually exclusive in compare menu

In compare mode with a Kimi external model selected, choosing Web search from the new + menu only flips toolsEnabled, bypassing the Kimi handling used by the Search pill below that also disables reasoning. The backend Kimi web-search path sends thinking disabled, so this menu path can leave the Thinking pill checked while the request actually runs without thinking, giving users misleading state and different results when they enable search here.

Useful? React with 👍 / 👎.

The docked composer omitted baseUrl, so a custom Gemini OpenAI-compat
gateway still advertised the native thinking ladder the backend cannot
honor. Pass selectedExternalProvider.baseUrl like the compare composer
so the resolver hides it.
- Plus side-menu items hover grey in light mode, not the green accent
- Thinking pill hovers green like the Search and Code pills
- The plain Thinking toggle shows an x on hover when active, matching
  Search and Code; the effort dropdown trigger keeps its bulb
The x filled the icon slot, so the wider Code chevron gave a bigger x
than Search and Compare. Pin it to a fixed 15px, centered, so every
pill's x matches.
- Accept svg, source code and many text/config files as drag-and-drop
  or picked attachments, matched by extension since their MIME is
  unreliable; html keeps its own adapter
- Active (green) side-menu items keep their text and icon color on
  hover instead of switching to the accent color
- Composer surface uses Gemini's soft centered shadow 0 0 20px rgba(0,0,0,0.04)
The inactive thinking pill has no label, so its flex row collapsed to
the icon height and the hover box looked short. Reserve one text line
(min-height: 1lh + padding) so it matches the Search and Code pills.
- Open the MCP servers dialog directly from the composer plus menu
- Redesign the drag-and-drop affordance Gemini style, drop the badge and border, make the whole chat page a drop target
- Swap in Hugeicons for the RAG, attachment chip and new project icons
- Add time-based randomized welcome greetings, each matched to a fitting sloth
Bring in the Canvas/artifacts feature and fit it into the redesigned composer.

- Keep the redesigned right-side controls and plus menu
- Add an Artifacts pill to the expanded composer row
- Wire the Canvas menu item to the artifacts toggle in both the main and compare composers
- Open the MCP servers dialog directly from the MCP menu item
- Keep the new artifact panel styles next to the composer styles

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d647f2b666

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1589 to +1592
<DropdownMenuItem
className={toolsEnabled ? "text-primary font-medium" : undefined}
onSelect={() => setToolsEnabled(!toolsEnabled)}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable unsupported tools in the plus menu

When a model is already loaded but does not support web search (and similarly Code just below), the visible pill is disabled via the capability gate, but this menu item still toggles and persists toolsEnabled. In that context the backend drops the unsupported tool for the current turn, while the menu shows it checked and the persisted flag can unexpectedly enable web search after switching to a capable model; apply the same loaded-model capability guard here while still allowing preselection when no model is loaded.

Useful? React with 👍 / 👎.

- Label the toggle Canvas everywhere, matching the plus menu
- Stop greying out the Canvas menu item; it toggles like the other items
- Only show the Canvas pill in the composer row once it is turned on, since it is less central than Search and Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5d07c1f10

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +181 to +184
for (const file of files) {
aui
.composer()
.addAttachment(file)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip page drops when the composer is hidden

In compare mode each pane renders Thread hideComposer={true}, but this page-wide drop handler still consumes drops over the pane and adds files to that pane's hidden assistant-ui composer. The shared compare composer builds its send content from its own pendingImages/pendingAudio, so a user dropping an image over either compare pane gets no visible attachment and the file is not sent; guard this handler when hideComposer is true or route drops to the shared composer.

Useful? React with 👍 / 👎.

Comment on lines +604 to +606
toolsEnabled ||
codeToolsEnabled ||
imageToolsEnabled;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include Canvas in the expansion trigger

When the user enables Canvas from the new plus menu while the single-chat composer is still one line and no other tools are active, composerExpanded remains false because it doesn't include artifactsEnabled. Since ArtifactsToggle is only rendered inside the expanded left controls, Canvas stays enabled without the visible Canvas pill or one-click disable path that this redesign expects.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if (hasCompareHandles && !isGeneralizedCompare) {
toast.error("Pick a model in each pane to compare", {
description:
"Use the model dropdown above each pane, then send your prompt.",
});
return;

P1 Badge Allow LoRA compare sends through the shared composer

When compare mode is the LoRA/base path, LoraCompareContent renders SharedComposer without model1/model2 props and registers handles named base/lora, so hasCompareHandles is true while isGeneralizedCompare is false. This new guard therefore rejects every LoRA compare prompt with “Pick a model in each pane to compare” instead of falling through to the existing simultaneous handle.append(content) path, making the base-vs-LoRA compare workflow unusable.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@Imagineer99

Copy link
Copy Markdown
Collaborator

tested and works as expected.

Speed up the composer plus icon morph from 480ms to 300ms.

Add row-gap on the expanded composer line so the space between the text
and the controls row stays the same whether the box expanded from
wrapped text or from a toggle being on. The gap sits on the line, not the
input, so the placeholder max-height clamp never crops it.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

Pushed a small follow-up to the composer:

  • Sped up the plus-to-x icon morph from 480ms to 300ms so it feels snappier.
  • Fixed the two-row spacing. The gap between the text and the controls row now stays consistent whether the composer expanded from wrapped text or from a toggle being switched on. Previously the bottom padding got cropped by the empty input's max-height clamp, so the toggles sat too close to the placeholder when only a tool was active. Moved the spacing to row-gap on the line so it is never clipped.

danielhanchen and others added 3 commits June 3, 2026 12:19
Persisted Search/Code/Canvas/MCP toggles were surfacing the composer pill row on a fresh page load before any model was selected, so an empty composer looked different from the clean just-ejected state. Gate the composerExpanded tool checks on modelLoaded so a model-less composer stays collapsed, while saved preferences still apply the moment a model loads.
Hide the placeholder RAG entry from the composer plus menu in both single chat and compare until the feature is ready, and drop the now-unused DatabaseIcon import.
Selecting Web search, Code, Canvas or MCP from the + menu with no model
loaded did nothing visible: the toggle turned on but the composer never
expanded, so the pill stayed hidden. Drop the model-loaded gate from the
expand check so an active tool always surfaces its pill.

Align MCP with the Search/Code pattern too: grey it out only when a loaded
model lacks tool support, so MCP stays toggleable and the pill stays
clickable before a model is loaded instead of looking disabled.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

Pushed a fix for the composer tool toggles when no model is loaded.

Before this, selecting Web search, Code, Canvas or MCP from the + menu with no model loaded turned the toggle on but the composer never expanded, so the pill stayed hidden. MCP was worse: it was greyed out and could not be toggled at all.

Changes:

  • The composer now expands whenever a tool is active, regardless of whether a model is loaded, so the pill always surfaces when you select it.
  • MCP now follows the same gating as Search and Code: it greys out only when a loaded model lacks tool support, so it stays toggleable and the pill stays clickable before a model loads.

Note: tools left on from a previous session now also show on a fresh composer before a model loads, which is the intended behavior here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8cb707c24a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1648 to +1650

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear the active thread when opening a project

When a user chooses a recent project from the composer menu while viewing an existing thread, this path switches the URL to the project landing page but leaves activeThreadId pointing at the old chat. Other project-entry paths clear the thread first; without that, the header/sidebar can continue to reflect the stale chat and later bare /chat navigation can reopen it instead of staying on a fresh project view.

Useful? React with 👍 / 👎.

@danielhanchen
danielhanchen merged commit 37fd76a into main Jun 3, 2026
32 of 33 checks passed
@danielhanchen
danielhanchen deleted the studio-composer-clean branch June 3, 2026 13:07
rhsCZ pushed a commit to rhsCZ/unsloth that referenced this pull request Jun 4, 2026
Resolve conflicts from the chat composer redesign (unslothai#5891) and related main
changes while preserving the RAG feature:

- thread.tsx: keep both the page-wide drag-and-drop handlers and the
  composer-dock height measurement; re-add the RAG pill to the redesigned
  pill row (wrapped in PillGlyph) and count it in pillsCompact; the document
  bar, preview mount and KnowledgeBaseToolUI wiring carried over unchanged.
- shared-composer.tsx: re-add the RAG pill to the redesigned pill layout and
  include it in the pillsCompact count.
- test_desktop_auth.py: add rag_router to main's routes-module stub.
- package-lock.json: regenerate against the merged package.json.
@LeoBorcherding LeoBorcherding mentioned this pull request Jun 5, 2026
6 tasks
LeoBorcherding pushed a commit to LeoBorcherding/unsloth that referenced this pull request Jun 5, 2026
* studio: redesign chat composer

Reworks the new-chat composer and the compare composer into a single
rounded pill surface with a softer, lighter look.

- New welcome screen with a time-of-day sloth mascot and a lighter
  heading.
- One rounded composer surface with a soft drop shadow. The input grows
  inline as you type and collapses back to a single row when cleared.
- Tools and attachments live in a single plus menu; the thinking control
  is a compact pill with a reasoning-effort submenu.
- Inlined glyphs for the thinking, send, and dictate controls, kept in
  sync across the main and compare composers.
- Toast notifications match the composer surface: no border line, the
  same drop shadow, and the same dark surface color, with a ring-less
  close button.
- Dark mode: the side-menu shadow blends into the background, hovered
  menu rows read clearly, and their roundness matches light mode.
- Composer styles use dedicated unsloth- prefixed classes so compare
  mode keeps its own stacked layout.

* studio: sync compare-composer reasoning state and harden compare id

- Compare composer: keep "Preserve thinking" consistent with reasoning,
  matching the main composer. Enabling it now turns reasoning on, and
  disabling reasoning (the None option or the Thinking toggle) turns it
  off, so the invalid "preserve on while thinking off" state can't occur.
- Guard crypto.randomUUID in the Compare action. It is undefined in
  non-secure contexts (HTTP over a LAN IP) and would throw; fall back to
  a timestamped random id, matching createNavigationNonce.

* studio: reflect pre-selected Search/Code tools when no model is loaded

The Search and Code pills only lit up when the tool was usable right now
(a model loaded and capable), so a tool turned on from the + menu showed
as off in the pill while the menu showed it on. toolsEnabled is persisted
and takes effect once a capable model loads, so the pill should reflect it.
The pills now disable only when a loaded model lacks the capability, and
otherwise reflect the selected state. Applied to the main and compare
composers.

* Studio: link MCP Servers heading to its PR and fix composer pill cursors

Make the "MCP Servers" heading in the chat Configuration sheet link to the
MCP PR, keeping the chevron as the toggle. The label and chevron are rendered
as siblings so we don't nest an <a> inside a <button>.

Also add cursor-pointer to the composer pills and the thinking pill so hovering
a clickable pill shows the hand cursor instead of the default arrow.

* Studio: refine chat composer and add compare-mode parity

- Composer expands to two rows only once the input wraps to a second line,
  not on the first keystroke. Re-measure the autosize textarea on the width
  swap so expanding no longer leaves a stray blank row.
- Light-mode composer shadow now matches Gemini's soft elevation.
- Plus menu: replace Canvas with a More submenu (Canvas, Compare chat, RAG)
  and add Code above MCP. Active Web search/Code items use medium weight.
- Compare mode: the plus side menu, Search/Code toggles, and a Compare exit
  pill now match single chat, with the thinking control on the right.
- Projects menu entries link to their tracking PR (unslothai#5725).
- Add cursor-pointer to the composer plus button.

* studio: refine composer controls and chat search shadow

- Active tool pills show an x on hover to signal click-to-disable
- Plus button rotates into an x when the tools menu opens
- Composer surface uses a 32px radius and a taller single-line height
- Even, ChatGPT-style spacing between the plus and tool pills in both the single and compare composers
- Send and mic circles resized and spaced, with the arrow centered in the circle
- Chat search box gets a borderless, soft Gemini-style shadow

* studio: size the pill hover x to match the icon it replaces

Cross-engine checks (Chromium, Firefox, WebKit) flagged the active-pill
hover x as a fixed 14px, so it popped smaller than the 19px Code icon.
Fill the glyph slot instead so the x tracks whatever icon it covers.

* studio: do not persist Kimi search/thinking mutual-exclusion in single composer

The single-chat composer flipped the other control off when toggling
search or thinking on Kimi, but without { persist: false }, so it
overwrote the user's saved preference. Match shared-composer and keep
the side effect session-only.

* studio: pointer cursor on model selector trigger and menu items

Add scoped marker classes so the model picker trigger and every
clickable element in its menu (tabs, model rows, delete, eject) show a
pointer cursor; disabled items stay not-allowed.

* studio: pass baseUrl when resolving reasoning caps in single composer

The docked composer omitted baseUrl, so a custom Gemini OpenAI-compat
gateway still advertised the native thinking ladder the backend cannot
honor. Pass selectedExternalProvider.baseUrl like the compare composer
so the resolver hides it.

* studio: grey side-menu hover, green pill hover, thinking hover x

- Plus side-menu items hover grey in light mode, not the green accent
- Thinking pill hovers green like the Search and Code pills
- The plain Thinking toggle shows an x on hover when active, matching
  Search and Code; the effort dropdown trigger keeps its bulb

* studio: make the pill hover x a uniform size

The x filled the icon slot, so the wider Code chevron gave a bigger x
than Search and Compare. Pin it to a fixed 15px, centered, so every
pill's x matches.

* studio: broaden chat attachments, fix active hover color, gemini shadow

- Accept svg, source code and many text/config files as drag-and-drop
  or picked attachments, matched by extension since their MIME is
  unreliable; html keeps its own adapter
- Active (green) side-menu items keep their text and icon color on
  hover instead of switching to the accent color
- Composer surface uses Gemini's soft centered shadow 0 0 20px rgba(0,0,0,0.04)

* studio: keep the thinking pill full height when icon-only

The inactive thinking pill has no label, so its flex row collapsed to
the icon height and the hover box looked short. Reserve one text line
(min-height: 1lh + padding) so it matches the Search and Code pills.

* studio: refine composer menu, drop overlay and greetings

- Open the MCP servers dialog directly from the composer plus menu
- Redesign the drag-and-drop affordance Gemini style, drop the badge and border, make the whole chat page a drop target
- Swap in Hugeicons for the RAG, attachment chip and new project icons
- Add time-based randomized welcome greetings, each matched to a fitting sloth

* studio: rename artifacts toggle to Canvas and make it opt-in

- Label the toggle Canvas everywhere, matching the plus menu
- Stop greying out the Canvas menu item; it toggles like the other items
- Only show the Canvas pill in the composer row once it is turned on, since it is less central than Search and Code

* studio: wire Canvas and MCP composer toggles, even out the pill row

- Open the MCP servers dialog from the menu, or toggle MCP on/off once a server is enabled
- Force MCP off when no server is enabled, so the toggle stays honest
- Show Canvas and MCP as opt-in pills that appear in the order they were toggled on
- Expand the composer and light up the pill when Canvas or MCP is on, like Search and Code
- Keep Compare directly after Code in the compare composer
- Use the same Code icon on both composers and give every pill an even icon slot

* studio: tidy composer toggle row and fix MCP enable/disable lifecycle

- Enable MCP automatically after a server is configured via the toggle flow
- Force MCP off everywhere once the last enabled server is removed
- Collapse the pill labels to icons only when more than 4 pills show, keeping Compare labelled
- Order Compare first in compare mode, before Search and Code
- Use the same Code icon and an even 19px icon slot across both composers
- Match the compare composer surface padding and send button inset to normal chat

* studio: revert compare composer padding change that cramped the input

Matching the surface padding to normal chat clipped the textarea text and
left a white strip on top. Restore the compare composer's own padding, which
gives proper top spacing. The send button inset fix stays.

* studio: center welcome greeting and soften composer scrollbar

Center the sloth and title together over the composer instead of
shifting the row left, which left the greeting sitting off to the side.

Keep the composer textarea scroll thumb faint by default and only darken
it when the thumb is hovered or dragged, so a tall draft no longer shows
a heavy dark rail.

* studio: match composer plus-menu tool gating to the pills

The new plus-menu tool entries did not carry the gating the visible pills
already enforce, so the menu and pills could disagree about a loaded
model's capabilities.

- Web search and Code menu items now disable when a loaded model lacks
  the capability, while still allowing preselection with no model loaded.
- Enabling Web search from the menu on a Kimi model now flips thinking
  off as a session-only change, since Kimi forbids search and thinking
  together. This matches the Search pill.
- Added an Images menu item, shown only for image-generation models and
  disabled until a model loads, so a short prompt has an entry point.

Applied to both the single-chat and compare composers.

* studio: round the active-pill hover x and even out pill padding

The hover x sat bare and the trailing label was tighter to the pill edge
than the leading icon, so the pill looked lopsided.

- Give the hover x a soft circular background that fills the icon slot,
  matching the ChatGPT-style toggle and the icon it replaces.
- Add a little more trailing padding so the label and the leading icon
  have even breathing room, and keep icon-only compact pills symmetric.

* studio: nudge the thinking bulb icon up by 0.5px

Bump the thinking lightbulb from 15px to 15.5px in the single-chat and
compare composers so it sits a touch larger next to the other controls.

* studio: drop the hover x circle on icon-only pills

When pills collapse to icon-only, the circle around the hover x is too
cramped in the small chip, so show a bare x there and keep the circle
only on the full-width labelled pills.

* studio: space the compare send button like normal chat

In compare mode the Thinking control sat right against the send button.
Match the normal composer's control spacing (gap-1.5 plus a send margin)
so Thinking has the same breathing room before send. The send button
keeps its 14px inset, so its position is unchanged.

* studio: make collapsed pill hover a circle, not a wide pill

Icon-only pills were wider than tall, so their rounded-full hover
highlight read as a fat rounded rectangle. Make the compact button a
square and center the glyph so the hover (and the x it reveals) sits in
a clean circle.

* studio: fix compare pane drops and audio picker lifetime

- Skip the page-level drop handler when the composer is hidden, so files
  dropped on a compare pane are not swallowed by a hidden composer; the
  shared compare composer keeps handling drops through its own dropzone.
- Build the audio file input on document.body instead of inside the plus
  menu, so the menu closing on select no longer unmounts the input before
  the OS picker returns and drops the file.

* studio/chat: stop projects list from white-screening on older backends

The projects list API returned data.projects directly, so a backend that
omits the field handed back undefined. useChatProjects cached that value,
then the next mount read undefined.length and crashed the whole chat page.

Default the projects and threads list APIs to an empty array and keep the
hook null-safe so a bad response can never poison the cache.

* studio/chat: align MCP dropdown with the + menu and add a chevron

Reuse the + menu surface (unsloth-plus-menu) for the MCP dropdown: rounded
corners, narrower width, neutral grey hover, and enabled rows shown as green
text with a right-aligned check instead of the emerald underlay. Add a
chevron to the MCP pill so it reads as openable, matching the Thinking pill.

* studio/chat: make MCP an opt-in pill and fix its dropdown placement

- MCP is back in the + menu as a toggle. The pill now only shows in the
  composer when MCP is on, matching Canvas, instead of always sitting there.
- The dropdown follows the composer side like the + menu (opens down in the
  welcome composer, up when docked) rather than always opening upward.
- Drop the dropdown caret when pills collapse so the icon is not squished.
- Stop force-syncing mcpEnabledForChat to the server count; the + menu owns it.

* studio/chat: MCP expands the composer, drop sidebar Compare, tidy scrollbars

- Toggling MCP now expands the composer and shows the tool pills, the same as
  Canvas, instead of leaving the row collapsed.
- Remove the Compare item from the sidebar now that it lives in the + menu, and
  point the compare tour step at the side-by-side view instead of the old button.
- Both sidebars only show their scrollbar on hover, and run settings reserves
  the scrollbar gutter so the close button no longer shifts when it appears.

* studio/chat: tighten toggle gap, fix run-settings close button, collapsed Train

- Reduce the composer toggle gap by 2px (gap-1 to gap-0.5) in both composers.
- Move the run settings header out of the scroll area so the close button keeps
  its position whether or not the scrollbar shows, and sits flush with the
  topbar open button again instead of shifting left.
- Surface Train as an icon in the collapsed sidebar (it already has a labelled
  section when expanded).

* studio/chat: tighten Thinking pill X padding, create projects inline

- The Thinking pill used px-2.5, so the hover X sat further in than the left
  pills. Match their pl-2 so the X lines up.
- The + menu New project now opens a create dialog and jumps straight to the
  new project, instead of routing to the projects list. Shared by both
  composers via a small NewProjectDialog.

* studio/chat: soften account menu, hover scrollbars, show collapsed chevrons

- Account menu drops its border ring for the composer's soft shadow and opens
  centered over its trigger.
- Settings and search reuse the hover-only scrollbar via a shared
  hover-scrollbar class, matching the sidebars.
- Train and Recents keep their chevron visible while collapsed so it is clear
  they can be expanded.

* studio/chat: roomier, more rounded account menu

Widen the account menu, add more left and right padding on the rows, bump the
row height and text a touch, and round the corners more, closer to the GPT
account menu.

* studio/chat: trim account menu width and nudge it up 2px

Pull the account menu in slightly on the left and right (narrower box, a touch
less row padding) and lift it 2px higher above the trigger.

* studio/settings: drop outline ring, circular close hover, pointer cursors

- Remove the settings dialog outline ring, keeping just the soft shadow.
- The close button hover is now a circle instead of a rounded rectangle.
- Every clickable control in the settings dialog uses a pointer cursor.

* studio/chat: bump MCP pill icon to 14.5px

Nudge the MCP icon up 0.5px so it sits even with the other pill glyphs.

* studio/chat: bump MCP pill icon to 15px

Nudge the MCP icon up another 0.5px.

* studio/settings: add a Settings title above the tabs

Put a Settings heading at the top of the sidebar so the tabs sit below it,
matching the Claude settings layout. Hidden on mobile where the nav is a row.

* studio/settings: rounder tab hover, bigger title, less-round search dialog

* studio/sidebar: round nav row hover boxes 2px more (10px to 12px)

* studio: drop settings dark shadow + divider, add tab left padding, tune hover roundness

* studio/model-selector: roomier padding, borderless box, rounder hover rows; settings divider light-only

* studio/search: match chat box shadow (soft light, none dark)

* studio/sidebar: borderless chat context menus, rename submenu to Projects with folder-export icon

* studio/model-selector: match light corner radius in dark, drop dark shadow, more visible dark hover

* studio/sidebar: chat context menu matches + side menu styling; relabel submenu Move to project

* studio: borderless message export menu (no dark shadow), match dark corner radius to light on export menu and settings

* studio/sidebar: open chat options menu GPT-style (down-right) and widen so Move to project fits one line

* studio/chat: message export menu uses the chatbox shadow in light mode

* studio/sidebar: narrow chat options menu slightly (w-60 to w-56)

* studio: unify all download icons to Hugeicons download-01; round profile button hover 1px more

* studio/run-settings: bump header to 16px

* studio/sidebar: trim chat options menu width slightly (w-56 to 216px)

* studio/sidebar: trim chat options menu width to w-52

* studio/profile: camera-01 Hugeicons glyph and chatbox shadow on avatar button

* studio: match dark-mode corner radius to light globally (single --radius token)

* studio/recipes: borderless New Recipe menu with chatbox shadow in light, none in dark

* studio: borderless dropdowns globally, chatbox shadow in light, none in dark

* studio: extend borderless + chatbox/none shadow to select, combobox and popover overlays

* studio/mcp: nudge MCP dropdown radius to 20px so its wider box reads as round as the + menu

* studio: restore dark dropdown shadow to avoid same-color merge; greet name ~1/3 of lines; bigger sloth + more gap

* studio/train: active tab is a borderless pill (no underline), roomier padding, more tab gap and bottom spacing

* studio/chat: nudge welcome up ~5px (still vh-based) and trim sloth image to 44px

* studio/train: active tab pill is white with chatbox shadow in light, taller padding

* studio/chat: welcome offset to calc(30vh - 10px)

* studio/chat: welcome offset to 28vh (drop the -10px)

* studio/chat: tighten sloth-to-text gap by 1px (16px to 15px)

* studio/train: revert light active pill to grey fill, drop white bg + shadow

* studio: app-wide hand cursor on every clickable control (disabled excluded)

* studio/chat: welcome offset to 26vh

* studio/chat: welcome offset to 28vh

* studio/chat: harden project and thread list guards against non-array payloads

* studio/sidebar: give the profile row more height and breathing room

* studio/sidebar: trim the profile row top and bottom padding slightly

* studio/sidebar: reduce Train and Recents section label size slightly

* studio/sidebar: trim the profile row top and bottom padding a touch more

* studio/sidebar: enlarge the profile hover area top and bottom

* studio/sidebar: increase profile hover roundness by 1px

* studio/sidebar: trim the profile row top and bottom padding slightly

* studio/sidebar: trim the profile row top and bottom padding slightly

* studio/chat: cache composer line metrics so wrap detection runs once, not per keystroke

* studio/chat: restore the prior view when exiting compare opened from the + menu

* studio/tests: drive Compare from the composer + menu after it moved out of the sidebar

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* studio/tests: open Compare from the composer + menu in the extra UI suite too

* studio: fix chat dictation microphone access

* studio: snappier plus-to-x spin and steady composer expand gap

Speed up the composer plus icon morph from 480ms to 300ms.

Add row-gap on the expanded composer line so the space between the text
and the controls row stays the same whether the box expanded from
wrapped text or from a toggle being on. The gap sits on the line, not the
input, so the placeholder max-height clamp never crops it.

* studio: only show composer tool pills once a model is loaded

Persisted Search/Code/Canvas/MCP toggles were surfacing the composer pill row on a fresh page load before any model was selected, so an empty composer looked different from the clean just-ejected state. Gate the composerExpanded tool checks on modelLoaded so a model-less composer stays collapsed, while saved preferences still apply the moment a model loads.

* studio: hide RAG composer menu item temporarily

Hide the placeholder RAG entry from the composer plus menu in both single chat and compare until the feature is ready, and drop the now-unused DatabaseIcon import.

* studio: let composer tools pre-select before a model loads

Selecting Web search, Code, Canvas or MCP from the + menu with no model
loaded did nothing visible: the toggle turned on but the composer never
expanded, so the pill stayed hidden. Drop the model-loaded gate from the
expand check so an active tool always surfaces its pill.

Align MCP with the Search/Code pattern too: grey it out only when a loaded
model lacks tool support, so MCP stays toggleable and the pill stays
clickable before a model is loaded instead of looking disabled.

---------

Co-authored-by: Unsloth <michaelhan@Michaels-MacBook-Pro.local>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: wasimysaid <wasimysdev@gmail.com>
Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com>
Co-authored-by: Daniel Han <23090290+danielhanchen@users.noreply.github.com>
danielhanchen added a commit that referenced this pull request Aug 19, 2026
* Scroll into view before a forced click

`Compare tab: send to two panes` failed on a macOS runner with

    Locator.click: Element is outside of the viewport

then a cascade of TargetClosedError as the browser came down behind it. The menu
item existed and was found. It was simply below the fold: a Mac runner's window
is shorter than a Linux one and Compare sits at the bottom of a long menu.

`click(force = True)` turns off actionability checks, which is exactly what these
call sites want against a menu whose overlay would otherwise intercept the click.
It also turns off the scroll that brings the element into the viewport, and
Playwright will not click a point it cannot reach. So the flag was doing two
things and only one of them was wanted.

The forced clicks are not new -- they arrived with the composer redesign in #5891
and have always worked on Linux. What changed is #9264, which made the Compare nav
be found reliably instead of sampled for: before that the step often gave up
earlier with "Compare nav not found" and never reached the click. That PR is
correct; it surfaced this rather than causing it, the same way #9283 was going to
surface the picker's context-pin assertion once the shard stopped dying in apt.

click_forced keeps force and adds the scroll back. The scroll is best effort: an
element that cannot be scrolled -- fixed position, zero size -- should still reach
the click and fail there with Playwright's own message, rather than here with a
scrolling one that names the wrong problem.

Applied to all 17 forced clicks across the three drivers, not just the one that
failed. They are the same hazard on the same runners, and the guard would be
worth little if the file it was written for were the only one obeying it. The
guard is a source scan for the bare shape, so a new one fails here rather than
twenty minutes into a Mac job.

Driven against a fake locator: these tests run on browserless lanes, and the
ordering is the whole contract, so a stand-in that records call order tests it
exactly -- scroll before click, force preserved, a failing scroll not swallowing
the click, and a failing click still propagating.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants