Skip to content

Studio: UI polish for sidebar, menus, hub and toasts#6288

Merged
shimmyshimmer merged 25 commits into
mainfrom
studio-ui-polish
Jun 13, 2026
Merged

Studio: UI polish for sidebar, menus, hub and toasts#6288
shimmyshimmer merged 25 commits into
mainfrom
studio-ui-polish

Conversation

@shimmyshimmer

@shimmyshimmer shimmyshimmer commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

A round of UI polish for Unsloth Studio. Everything here is scoped to the studio frontend.

Changes

Chat sidebar

  • Add a Pinned section above Recents. Chats can be pinned or unpinned from the row menu, and each pinned row shows an unpin button on hover.
  • Align the Recents and Pinned section labels with Train (same font and chevron size).
  • Switch the chat Archive menu icon to archive-03.

Menus

  • Pull dropdown separators in so they match the item width instead of bleeding to the edges.
  • Even out the projects switcher padding and corner radius so it matches the composer menu.
  • Match the account menu divider width to the row hover width.

Hub

  • Equalize the Format, Capability and Sort filter widths in the models toolbar so the Hugging Face search field has more room.
  • Make the quantization selector hover a fully rounded pill.

Profile

  • Add a set of sloth stickers you can pick as a profile picture, shown below the picture shape toggle in Settings > Profile. Only squarish, low-whitespace stickers are listed so each one fills the avatar frame cleanly.
  • Image avatars now sit on a neutral background so transparent stickers read cleanly in both the circle and rounded shapes.

Misc

  • Slightly larger corner radius on tooltips.
  • Less horizontal padding on the auth submit button.
  • Center the model load toast content so the top and bottom padding read evenly.

Notes

Pinned state is stored client side in localStorage, keyed by chat id, so it does not touch the chat database. Sloth picks reuse the existing avatar storage in localStorage in the same way, so they do not touch the backend either.

- Pinned chats: add a Pinned section above Recents with a Pin/Unpin menu
  action and a hover unpin button on each pinned row
- Sidebar: align the Recents and Pinned section labels with Train, and
  switch the chat Archive icon to archive-03
- Dropdown menus: pull separators in to match item width, and even out the
  projects menu padding and radius to match the composer menu
- Account menu: match the divider width to the row hover width
- Hub toolbar: equalize the format, capability and sort filter widths so
  the search field has more room
- Model run bar: make the quant selector hover a full pill
- Tooltips: slightly larger corner radius
- Auth: reduce horizontal padding on the submit button
- Toasts: center the model load content so top and bottom padding match
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@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: 70bd32b6a2

ℹ️ 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".


async function handleDelete(item: SidebarItem) {
try {
await deleteChatItem(item, undefined, () => {});

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 the active thread when deleting archived chats

When the user has an archived chat open (for example after selecting it from this dialog) and later deletes that same row here, deleteChatItem receives undefined for activeId. Its cleanup/navigation branch only runs when activeId === item.id, so the app stays on /chat?thread=... or /chat?compare=... for a thread that was just removed instead of resetting to a new chat. Pass the current thread/compare id into this call.

Useful? React with 👍 / 👎.

</button>
<button
type="button"
onClick={() => void handleDelete(item)}

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 Confirm before deleting archived chats

In Settings > Chat > Archived chats, clicking the trash icon immediately calls handleDelete, which permanently deletes the archived thread(s) without any confirmation. The archived-sidebar flow this replaces opened the shared delete confirmation first, so a stray click in this dense management list can now cause irreversible chat loss; route this action through a confirm dialog or reuse the existing delete confirmation.

Useful? React with 👍 / 👎.

Add a "Confirm before deleting" toggle in Settings > Chat > Data. When
turned off, deleting a chat from the sidebar skips the confirm dialog
and removes it instantly. The preference is stored client-side.

Add a separator above Archive in the chat row menu so Archive and
Delete read as a group separate from Export.

Drop the duplicate divider above "Clear all chats". The section already
draws a divide-y separator between rows, so the destructive row no
longer adds its own top border.
Hub catalog:
- Drop "inference" from the Hub subtitle.
- Make the HF token shield a circle in the header.
- Equal, narrower filter triggers that stay a fixed width; long labels
  truncate with an ellipsis instead of clipping.
- Tighter gap between the label and the dropdown chevron so one more
  character fits.
- Keep the toolbar on one row at desktop widths.
- Align the model list and row hover with the Discover tab, add right
  side breathing room.
- Make the dark-mode scrollbar lighter so it is easier to see.
- Filled size and quant chips no longer draw a border.
- Column divider uses the sidebar border color.
- Hub card extends into the bottom space.

Filters and channels:
- Add an MLX format option; detect MLX repos by library or tag.
- "Fine-tune ready" now includes 16bit safetensors, not only bnb-4bit.
- Shorten the curated channel descriptions.

Shared UI:
- New standard chevron icons (down and submenu right) shared across the
  Hub, dropdown menu, menubar, project switcher and model selector.
- Dialogs get more top and bottom padding and a larger heading.
- Guided Tour hides itself on pages with no tour.

Other pages:
- Export and Data Recipes use the same page padding as the Hub.

@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: 6cb93dc9ff

ℹ️ 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 +768 to +770
confirmDeleteChats
? setConfirmingDelete({ kind: "chat", item })
: void handleDeleteThread(item)

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 Route instant deletes through the delete handler

When Confirm before deleting is turned off, this branch calls handleDeleteThread outside the commitDelete path that catches failures and removes the pin. If deleteStoredChatThreads rejects due to a backend/storage error, deleteChatItem rolls back the optimistic tombstone but this ignored promise produces no toast; for pinned chats it also skips the unpinChat cleanup that the confirmed path performs. Use a shared delete helper here so the no-confirm path has the same error handling and cleanup.

Useful? React with 👍 / 👎.

Comment on lines +54 to 56
hint: "Checkpoints ready to fine-tune.",
owner: "unsloth",
query: "bnb-4bit",
idSuffix: "-bnb-4bit",
format: "checkpoint",

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 Restore bnb-4bit scope for fine-tune channel

This preset no longer passes the bnb-4bit query/id suffix that previously constrained “Fine-tune ready”; with only owner: "unsloth" plus format: "checkpoint", the Discover filter accepts every non-GGUF Unsloth safetensors/checkpoint repo rather than just the bnb-4bit fine-tune-ready checkpoints. In the Fine-tune ready channel this pollutes the list with unrelated Unsloth checkpoints, so keep the query/suffix constraint or use an equivalent strict filter.

Useful? React with 👍 / 👎.

{ value: "all", label: "All formats" },
{ value: "gguf", label: "GGUF" },
{ value: "checkpoint", label: "Checkpoints" },
{ value: "mlx", label: "MLX" },

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 Hide MLX until downloaded rows can match it

FORMAT_FILTER_OPTIONS is used for both Discover and On Device, but downloaded inventory rows still normalize model_format to only gguf, safetensors, adapter, checkpoint, or unknown. Since matchesFormat(row.modelFormat, "mlx") therefore has no matching inventory value, selecting MLX on the On Device tab filters the downloaded list to empty instead of showing cached/local MLX models; either add MLX to the inventory model format path or omit this option outside Discover.

Useful? React with 👍 / 👎.

Dialogs, alert dialogs and sheets now use a light blur with a slight
darken instead of a heavy black scrim, so popups stay legible without
dimming the whole screen. Edit System Prompt and Edit Chat Template
drop their custom overlays and share the same look.

Also limit the Fine-tune ready channel to checkpoints Unsloth can
actually fine-tune (drops fp8, nvfp4, w4a16 and similar), revert chip
borders so filled chips stay visible, nudge the Run icon to center it,
and shrink the submenu chevron a touch.
Rename a chat inline as a rounded pill on the row instead of opening a
dialog. Enter saves, Escape cancels, clicking away saves. Projects and
training runs keep the existing dialog.

Address review feedback:
- Route the no-confirm chat delete through the shared cleanup helper so
  it gets the same error toast and pin removal as the confirmed path.
- Archived chats now confirm before deleting (respecting the setting)
  and pass the active thread, so deleting the open chat resets to a new
  chat instead of leaving a stale thread URL.
- Only offer the MLX format filter on Discover, since downloaded
  inventory rows are never tagged mlx and would filter to empty.
- Tighten the non-finetunable name match so a dot-delimited token such
  as .tflite is also excluded from the Fine-tune ready channel.

@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: 482a845aa3

ℹ️ 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".

async function handleDelete(item: SidebarItem) {
try {
// Pass the active thread so deleting the open chat resets navigation.
await deleteChatItem(item, activeThreadId ?? undefined, (view) => {

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 Use the route compare id when deleting archived compares

When the archived row is a compare chat that was opened from this dialog (/chat?compare=...), the global activeThreadId does not hold the pair id (compare panes intentionally avoid writing it), so this call passes an id that cannot equal item.id. In that scenario deleteChatItem deletes the archived pair but skips its active-view cleanup/navigation branch, leaving the user on a URL for the deleted compare; derive the active id from the current route/search params as the sidebar does, or pass item.id when deleting the open archived row.

Useful? React with 👍 / 👎.

Rename now edits the title as plain highlighted text with no pill or
box. While the debounced sidebar refresh catches up after a rename the
row shows the new name optimistically, so the old name no longer flashes
back in. Recent chat titles now line up flush under the Recents label.
Add a "What should Unsloth call you?" preferred name that drives the
new-chat greeting, falling back to the first name then the login id.
Add a toggle to show the avatar as a full circle or a rounded
rectangle, applied everywhere the avatar appears.
Surface the Unsloth and package versions at the top of General, not
just in About. Both tabs share one StudioVersionSection so the version
fetch and markup live in a single place.
Add a curated set of sloth stickers users can choose as a profile
picture, shown below the picture shape toggle in Settings > Profile.

- Only squarish, low-whitespace stickers are listed so each one fills
  the avatar frame cleanly.
- Selecting one reuses the existing avatar persistence (localStorage),
  so it sits alongside the photo upload with no backend changes.
- Image avatars now get a neutral background so transparent stickers
  read cleanly in both shapes.
A transparent upload is now kept as WebP and shrunk to fit instead of
falling back to JPEG, which has no alpha and was painting a background
behind the image. The image avatar is also explicitly transparent.
Safari cannot encode WebP via canvas, so a transparent upload was
throwing instead of saving. Fall back to PNG, which keeps alpha and
works in every browser, before giving up. Still never uses JPEG.
Compare panes do not write the active thread to the store, so the pair
id only lives in the route search. Derive the open chat id from the
route (thread or compare) like the sidebar does, so deleting the open
archived compare resets to a new chat instead of a dead URL.

@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: 104bbabd49

ℹ️ 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 +532 to +535
if (event.key === "Enter") {
event.preventDefault();
skipRenameBlurRef.current = true;
void commitRename();

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 inline rename state on no-op Enter

When the user opens inline rename and presses Enter without changing the title (or after trimming back to the same title), commitRename() returns early because renameDirty is false, but this handler has already set skipRenameBlurRef. The following blur is then ignored, leaving renamingTarget set and the row stuck as an unfocused input until the user explicitly focuses it again and cancels; close the inline editor on no-op Enter or only suppress blur when the commit actually clears the target.

Useful? React with 👍 / 👎.

set({ confirmDeleteChats }),
}),
{
name: "unsloth_chat_preferences",

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 Reset delete confirmations with local preferences

This new delete-confirmation preference persists under unsloth_chat_preferences, but General > Reset all local preferences only removes the hard-coded PREFS_KEYS list in general-tab.tsx, and this key is absent there. If a user turns confirmations off and then uses reset to restore local UI defaults, the reload keeps instant chat deletion enabled instead of returning to the safer default; add this storage key to the reset list.

Useful? React with 👍 / 👎.

Comment on lines +263 to +267
<button
key={path}
type="button"
onClick={() => pickSloth(path)}
aria-pressed={selected}

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 Label the sloth avatar picker buttons

In the new sloth avatar grid, each option is a button with no accessible name: the only content is an image rendered with an empty alt. Screen-reader users therefore tab through a series of unnamed buttons and cannot tell which avatar they are choosing; add an aria-label or non-empty descriptive text for each option.

Useful? React with 👍 / 👎.

Drop the duplicate computer sloth without text on the screen (keeping
the Local one), the nature-screen sloth, and the gift sloth.
Use less right padding than left so the trailing chevron does not leave
a heavy gap on the right of the trigger.
- Give each sloth avatar button an accessible name so screen readers
  can tell the options apart.
- Reset all local preferences now clears the delete-confirmation pref,
  so it returns to the safe confirm default.
- A no-op Enter in inline rename now closes the editor instead of
  leaving the row stuck as an input with its blur suppressed.
More left padding than right and pull the chevron close to the label so
the trigger reads balanced around the text instead of heavy on the right.
Set the icon to size-6 so it sits proportionally in the media circle
instead of filling it.
Override the media circle to size-12 with a size-5 icon on this dialog
only, so it is more compact without changing the shared component.
@shimmyshimmer shimmyshimmer merged commit 2262e02 into main Jun 13, 2026
33 checks passed
@shimmyshimmer shimmyshimmer deleted the studio-ui-polish branch June 13, 2026 10:50

@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: f81a8cb317

ℹ️ 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".

"unsloth_tool_call_timeout",
"unsloth_chat_inference_params",
"unsloth_chat_collapsible_state",
"unsloth_chat_preferences",

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 Reset pinned chats with local preferences

When a user pins chats and then uses General > Reset all local preferences, the new usePinnedChatsStore state persists under unsloth_pinned_chats, but this reset list only clears the new delete-confirmation preference. That leaves the supposedly reset sidebar still showing the old Pinned section after reload; include the pinned-chat storage key here as well.

Useful? React with 👍 / 👎.

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.

1 participant