fix(studio): show chat sidebar menu on touch devices (#7297)
* fix(studio): show chat sidebar menu on touch devices Recents/Pinned chat row actions were hidden until hover, so iPad users could not open the kebab menu to delete chats. Reveal actions on coarse pointers using the same pattern as hub model rows. Fixes #7276 * Fix coarse-pointer sidebar row action visibility (#7276) Move the touch-device override into index.css after .sidebar-row-action so it wins the cascade. Arbitrary Tailwind media utilities on the element had equal specificity and were overridden by the base rule. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Scope coarse-pointer sidebar actions to chat rows (#7276) Only chat kebabs/unpin buttons that reserve touch padding get sidebar-touch-reveal, so project/run/nav rows stay hover-revealed. * Tighten comments * Reserve full kebab hit area on coarse-pointer unpinned rows --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
447f16f49a
commit
e2ccf4d376
3 changed files with 38 additions and 7 deletions
|
|
@ -901,8 +901,8 @@ export function AppSidebar() {
|
|||
: "group/recent-item relative";
|
||||
const actionClass =
|
||||
variant === "project"
|
||||
? "sidebar-row-action group-hover/project-chat-item:opacity-100 group-hover/project-chat-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
|
||||
: "sidebar-row-action group-hover/recent-item:opacity-100 group-hover/recent-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto";
|
||||
? "sidebar-row-action sidebar-touch-reveal group-hover/project-chat-item:opacity-100 group-hover/project-chat-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
|
||||
: "sidebar-row-action sidebar-touch-reveal group-hover/recent-item:opacity-100 group-hover/recent-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto";
|
||||
const buttonClass = cn(
|
||||
"sidebar-nav-btn h-[33px] cursor-pointer rounded-full pr-4 text-ui-14p5 leading-ui-19 tracking-nav font-medium",
|
||||
// pl-3 (12px) over the content's pl-1.5 (6px) = 18px, aligning the
|
||||
|
|
@ -912,13 +912,14 @@ export function AppSidebar() {
|
|||
isPinned && variant !== "project" && "gap-[8.5px]",
|
||||
variant === "project"
|
||||
? // Room for the hover pin quick-action plus the kebab.
|
||||
"group-hover/project-chat-item:pr-14 group-has-[.sidebar-row-action[data-state=open]]/project-chat-item:pr-8"
|
||||
"group-hover/project-chat-item:pr-14 group-has-[.sidebar-row-action[data-state=open]]/project-chat-item:pr-8 [@media(pointer:coarse)]:pr-14"
|
||||
: isPinned
|
||||
? // Pinned rows show an extra unpin button on hover, so reserve more room
|
||||
// (pr-8 when the menu is open keeps the unpin button clear of the title).
|
||||
"group-hover/recent-item:pr-16 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-8"
|
||||
"group-hover/recent-item:pr-16 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-8 [@media(pointer:coarse)]:pr-16"
|
||||
: // Hover room for the kebab only; title keeps one more character.
|
||||
"group-hover/recent-item:pr-6 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-6",
|
||||
// Touch rows clear the full always-visible kebab hit area (pr-10).
|
||||
"group-hover/recent-item:pr-6 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-6 [@media(pointer:coarse)]:pr-10",
|
||||
);
|
||||
|
||||
const isRenamingThis =
|
||||
|
|
@ -987,7 +988,7 @@ export function AppSidebar() {
|
|||
togglePinnedChat(item.id);
|
||||
}}
|
||||
aria-label={isPinned ? "Unpin chat" : "Pin chat"}
|
||||
className="sidebar-row-action is-unpin-action group-hover/project-chat-item:opacity-100 group-hover/project-chat-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
|
||||
className="sidebar-row-action sidebar-touch-reveal is-unpin-action group-hover/project-chat-item:opacity-100 group-hover/project-chat-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
|
||||
>
|
||||
<span className="sidebar-row-action-glyph">
|
||||
<HugeiconsIcon icon={isPinned ? PinOffIcon : PinIcon} strokeWidth={1.75} className="size-icon" />
|
||||
|
|
@ -1002,7 +1003,7 @@ export function AppSidebar() {
|
|||
togglePinnedChat(item.id);
|
||||
}}
|
||||
aria-label="Unpin chat"
|
||||
className="sidebar-row-action is-unpin-action group-hover/recent-item:opacity-100 group-hover/recent-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
|
||||
className="sidebar-row-action sidebar-touch-reveal is-unpin-action group-hover/recent-item:opacity-100 group-hover/recent-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
|
||||
>
|
||||
<span className="sidebar-row-action-glyph">
|
||||
<HugeiconsIcon icon={PinOffIcon} strokeWidth={1.75} className="size-icon" />
|
||||
|
|
|
|||
|
|
@ -1018,6 +1018,12 @@ html[data-chat-font] .aui-root {
|
|||
.sidebar-row-action {
|
||||
@apply absolute top-0 bottom-0 right-0 inline-flex cursor-pointer items-center justify-end pl-2 pr-1.5 opacity-0 pointer-events-none outline-none;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
/* Only chat rows reserve touch padding (#7276); other rows stay hover-revealed to avoid clipped labels. */
|
||||
.sidebar-row-action.sidebar-touch-reveal {
|
||||
@apply opacity-100 pointer-events-auto;
|
||||
}
|
||||
}
|
||||
.sidebar-row-action[data-state="open"] {
|
||||
@apply opacity-100 pointer-events-auto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ DATA_TAB = FRONTEND / "features/settings/tabs/data-tab.tsx"
|
|||
PROMPT_STORAGE = FRONTEND / "features/chat/prompt-storage/prompt-storage-dialog.tsx"
|
||||
|
||||
APP_SIDEBAR = FRONTEND / "components/app-sidebar.tsx"
|
||||
INDEX_CSS = FRONTEND / "index.css"
|
||||
THREAD = FRONTEND / "components/assistant-ui/thread.tsx"
|
||||
THREAD_SIDEBAR = FRONTEND / "features/chat/thread-sidebar.tsx"
|
||||
SHARED_COMPOSER = FRONTEND / "features/chat/shared-composer.tsx"
|
||||
|
|
@ -129,3 +130,26 @@ def test_expanded_titlebar_button_and_corner_match_sidebar_edge():
|
|||
'className="pointer-events-none absolute top-full size-3 -translate-x-px rounded-tl-[12px] border-l border-t border-sidebar-border bg-background"'
|
||||
in source
|
||||
)
|
||||
|
||||
|
||||
def test_chat_sidebar_row_actions_visible_on_coarse_pointers():
|
||||
"""unslothai/unsloth#7276: Recents chat kebab must be tappable on iPad."""
|
||||
sidebar_source = APP_SIDEBAR.read_text(encoding = "utf-8")
|
||||
css_source = INDEX_CSS.read_text(encoding = "utf-8")
|
||||
assert "renderChatSidebarItem" in sidebar_source
|
||||
block = sidebar_source.split("function renderChatSidebarItem", 1)[1].split("\n function ", 1)[
|
||||
0
|
||||
]
|
||||
assert "[@media(pointer:coarse)]:pr-10" in block
|
||||
assert "sidebar-touch-reveal" in block
|
||||
# Coarse-pointer visibility must come after .sidebar-row-action { opacity-0 }.
|
||||
coarse_idx = css_source.index("@media (pointer: coarse)")
|
||||
base_idx = css_source.index(".sidebar-row-action {")
|
||||
assert coarse_idx > base_idx
|
||||
coarse_block = css_source[coarse_idx : coarse_idx + 280]
|
||||
assert "sidebar-touch-reveal" in coarse_block
|
||||
assert "opacity-100" in coarse_block
|
||||
assert "pointer-events-auto" in coarse_block
|
||||
# Must not reveal every sidebar-row-action (project/run/nav rows lack padding).
|
||||
assert ".sidebar-row-action {\n\t\t\t@apply opacity-100" not in coarse_block
|
||||
assert ".sidebar-row-action.sidebar-touch-reveal" in coarse_block
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue