]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
ui: fix accessibility for hover-gated interactive elements assisted by claude(in...
authorSanjay Ahari <redacted>
Fri, 26 Jun 2026 10:55:38 +0000 (16:25 +0530)
committerGitHub <redacted>
Fri, 26 Jun 2026 10:55:38 +0000 (12:55 +0200)
tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte
tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserPending.svelte
tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationItem.svelte

index 636e93f221111a1fab2b2f2e5c490248c1cb50d4..c55dfdec7b82b672136fa795ebb7c05273030208 100644 (file)
@@ -33,7 +33,7 @@
 
        {#if !readonly && onRemove}
                <div
-                       class="absolute top-10 right-2 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100"
+                       class="absolute top-10 right-2 flex items-center justify-center opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100"
                >
                        <ActionIcon icon={X} tooltip="Remove" stopPropagationOnClick onclick={() => onRemove?.()} />
                </div>
index 4be582b39aecef7cbcf1213227b636430375f2c8..5c2913202c77fd362614edcaffdb7e73a64c254f 100644 (file)
@@ -56,7 +56,7 @@
                        <div class="relative flex h-6 items-center justify-between">
                                <div class="right-0 flex items-center gap-2 opacity-100 transition-opacity">
                                        <div
-                                               class="pointer-events-auto inset-0 flex items-center gap-1 opacity-0 transition-all duration-150 group-hover:opacity-100"
+                                               class="pointer-events-auto inset-0 flex items-center gap-1 opacity-0 transition-all duration-150 group-focus-within:opacity-100 group-hover:opacity-100"
                                        >
                                                <ActionIcon icon={Edit} tooltip="Edit" onclick={editCtx.handleEdit} />
                                                <ActionIcon icon={Trash2} tooltip="Delete" onclick={onDelete} />
index b1c2b78f65eaa4ccec0b99630ad5a3bfe6f1fbe7..2c1b9adf21ba908f03ab612c633c0ff24ccc8570 100644 (file)
@@ -39,7 +39,6 @@
                depth = 0
        }: Props = $props();
 
-       let renderActionsDropdown = $state(false);
        let dropdownOpen = $state(false);
 
        let isLoading = $derived(getAllLoadingChats().includes(conversation.id));
                }
        }
 
-       function handleMouseLeave() {
-               if (!dropdownOpen) {
-                       renderActionsDropdown = false;
-               }
-       }
-
-       function handleMouseOver() {
-               renderActionsDropdown = true;
-       }
-
        function handleSelect() {
                onSelect?.(conversation.id);
        }
 
-       $effect(() => {
-               if (!dropdownOpen) {
-                       renderActionsDropdown = false;
-               }
-       });
-
        onMount(() => {
                document.addEventListener('edit-active-conversation', handleGlobalEditEvent as EventListener);
 
        });
 </script>
 
-<!-- svelte-ignore a11y_mouse_events_have_key_events -->
-<button
-       class="group flex min-h-9 w-full cursor-pointer items-center justify-between space-x-3 rounded-lg py-1.5 text-left transition-colors hover:bg-foreground/10 {isActive
+<div
+       class="conversation-item group relative flex min-h-9 w-full items-center justify-between space-x-3 rounded-lg py-1.5 transition-colors hover:bg-foreground/10 {isActive
                ? 'bg-foreground/5 text-accent-foreground'
                : ''} px-3"
-       onclick={handleSelect}
-       onmouseover={handleMouseOver}
-       onmouseleave={handleMouseLeave}
-       onfocusin={handleMouseOver}
-       onfocusout={(e) => {
-               if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {
-                       handleMouseLeave();
-               }
-       }}
 >
+       <button
+               class="absolute inset-0 z-0 cursor-pointer rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
+               onclick={handleSelect}
+               aria-label={conversation.name}
+       >
+       </button>
        <div
-               class="flex min-w-0 flex-1 items-center gap-2"
+               class="pointer-events-none relative z-10 flex min-w-0 flex-1 items-center gap-2"
                style:padding-left="{depth * FORK_TREE_DEPTH_PADDING}px"
        >
                {#if depth > 0}
                                                <a
                                                        {...props}
                                                        href={RouterService.chat(conversation.forkedFromConversationId)}
-                                                       class="flex shrink-0 items-center text-muted-foreground transition-colors hover:text-foreground"
+                                                       class="pointer-events-auto flex shrink-0 items-center text-muted-foreground transition-colors hover:text-foreground"
                                                >
                                                        <GitBranch class="h-3.5 w-3.5" />
                                                </a>
                {#if isLoading}
                        <Tooltip.Root>
                                <Tooltip.Trigger>
-                                       <div
-                                               class="stop-button flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground"
+                                       <button
+                                               class="stop-button pointer-events-auto flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground"
                                                onclick={handleStop}
-                                               onkeydown={(e) => e.key === 'Enter' && handleStop(e)}
-                                               role="button"
-                                               tabindex="0"
                                                aria-label="Stop generation"
                                        >
                                                <Loader2 class="loading-icon h-3.5 w-3.5 animate-spin" />
 
                                                <Square class="stop-icon hidden h-3 w-3 fill-current text-destructive" />
-                                       </div>
+                                       </button>
                                </Tooltip.Trigger>
 
                                <Tooltip.Content>
                <TruncatedText text={conversation.name} class="text-sm font-medium" showTooltip={false} />
        </div>
 
-       {#if renderActionsDropdown}
-               <div class="actions flex items-center">
-                       <DropdownMenuActions
-                               triggerIcon={MoreHorizontal}
-                               triggerTooltip="More actions"
-                               bind:open={dropdownOpen}
-                               actions={[
-                                       {
-                                               icon: conversation.pinned ? PinOff : Pin,
-                                               label: conversation.pinned ? 'Unpin' : 'Pin',
-                                               onclick: (e: Event) => {
-                                                       e.stopPropagation();
-                                                       handleTogglePin();
-                                               }
-                                       },
-                                       {
-                                               icon: Pencil,
-                                               label: 'Edit',
-                                               onclick: handleEdit,
-                                               shortcut: ['shift', 'cmd', 'e']
-                                       },
-                                       {
-                                               icon: Download,
-                                               label: 'Export',
-                                               onclick: (e: Event) => {
-                                                       e.stopPropagation();
-                                                       conversationsStore.downloadConversation(conversation.id);
-                                               },
-                                               shortcut: ['shift', 'cmd', 's']
-                                       },
-                                       {
-                                               icon: Trash2,
-                                               label: 'Delete',
-                                               onclick: handleDelete,
-                                               variant: 'destructive',
-                                               shortcut: ['shift', 'cmd', 'd'],
-                                               separator: true
+       <div class="actions pointer-events-auto relative z-20 flex items-center">
+               <DropdownMenuActions
+                       triggerIcon={MoreHorizontal}
+                       triggerTooltip="More actions"
+                       bind:open={dropdownOpen}
+                       actions={[
+                               {
+                                       icon: conversation.pinned ? PinOff : Pin,
+                                       label: conversation.pinned ? 'Unpin' : 'Pin',
+                                       onclick: (e: Event) => {
+                                               e.stopPropagation();
+                                               handleTogglePin();
                                        }
-                               ]}
-                       />
-               </div>
-       {/if}
-</button>
+                               },
+                               {
+                                       icon: Pencil,
+                                       label: 'Edit',
+                                       onclick: handleEdit,
+                                       shortcut: ['shift', 'cmd', 'e']
+                               },
+                               {
+                                       icon: Download,
+                                       label: 'Export',
+                                       onclick: (e: Event) => {
+                                               e.stopPropagation();
+                                               conversationsStore.downloadConversation(conversation.id);
+                                       },
+                                       shortcut: ['shift', 'cmd', 's']
+                               },
+                               {
+                                       icon: Trash2,
+                                       label: 'Delete',
+                                       onclick: handleDelete,
+                                       variant: 'destructive',
+                                       shortcut: ['shift', 'cmd', 'd'],
+                                       separator: true
+                               }
+                       ]}
+               />
+       </div>
+</div>
 
 <style>
-       button {
+       .conversation-item {
                :global([data-slot='dropdown-menu-trigger']:not([data-state='open'])) {
                        opacity: 0;
                }
                        }
                }
 
-               &:is(:hover) .stop-button {
+               &:is(:hover) .stop-button,
+               &:focus-within .stop-button {
                        :global(.stop-icon) {
                                display: block;
                        }