Skip to content

Commit 3a2bdcd

Browse files
Improve Mobile UI for dialogs and action dropdowns (ggml-org#16222)
* fix: Always show conversation item actions * feat: Improve Alert Dialog and Dialog mobile UI * feat: Add settings reset to default confirmation * fix: Close Edit dialog on save * chore: update webui build output * webui: implement proper z-index system and scroll management - Add CSS variable for centralized z-index control - Fix dropdown positioning with Settings dialog conflicts - Prevent external scroll interference with proper event handling - Clean up hardcoded z-index values for maintainable architecture * webui: ensured the settings dialog enforces dynamic viewport height on mobile while retaining existing desktop sizing overrides * feat: Use `dvh` instead of computed px height for dialogs max height on mobile * chore: update webui build output * feat: Improve Settings fields UI * chore: update webui build output * chore: update webui build output --------- Co-authored-by: Pascal <[email protected]>
1 parent 66bb798 commit 3a2bdcd

File tree

12 files changed

+137
-17
lines changed

12 files changed

+137
-17
lines changed

tools/server/public/index.html.gz

829 Bytes
Binary file not shown.

tools/server/webui/src/app.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
--sidebar-ring: oklch(0.708 0 0);
4040
--code-background: oklch(0.225 0 0);
4141
--code-foreground: oklch(0.875 0 0);
42+
--layer-popover: 1000000;
4243
}
4344

4445
.dark {

tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsDialog.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,8 @@
362362

363363
<Dialog.Root {open} onOpenChange={handleClose}>
364364
<Dialog.Content
365-
class="z-999999 flex h-[100vh] flex-col gap-0 rounded-none p-0 md:h-[64vh] md:rounded-lg"
365+
class="z-999999 flex h-[100dvh] max-h-[100dvh] min-h-[100dvh] flex-col gap-0 rounded-none p-0
366+
md:h-[64vh] md:max-h-[64vh] md:min-h-0 md:rounded-lg"
366367
style="max-width: 48rem;"
367368
>
368369
<div class="flex flex-1 flex-col overflow-hidden md:flex-row">
@@ -441,7 +442,7 @@
441442
</div>
442443
</div>
443444

444-
<ScrollArea class="max-h-[calc(100vh-13.5rem)] flex-1">
445+
<ScrollArea class="max-h-[calc(100dvh-13.5rem)] flex-1 md:max-h-[calc(100vh-13.5rem)]">
445446
<div class="space-y-6 p-4 md:p-6">
446447
<div>
447448
<div class="mb-6 flex hidden items-center gap-2 border-b border-border/30 pb-6 md:flex">

tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import * as Select from '$lib/components/ui/select';
66
import { Textarea } from '$lib/components/ui/textarea';
77
import { SETTING_CONFIG_DEFAULT, SETTING_CONFIG_INFO } from '$lib/constants/settings-config';
8-
import { IsMobile } from '$lib/hooks/is-mobile.svelte';
98
import { supportsVision } from '$lib/stores/server.svelte';
109
import type { Component } from 'svelte';
1110
@@ -17,8 +16,6 @@
1716
}
1817
1918
let { fields, localConfig, onConfigChange, onThemeChange }: Props = $props();
20-
21-
let isMobile = $state(new IsMobile());
2219
</script>
2320

2421
{#each fields as field (field.key)}
@@ -33,7 +30,7 @@
3330
value={String(localConfig[field.key] ?? '')}
3431
onchange={(e) => onConfigChange(field.key, e.currentTarget.value)}
3532
placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`}
36-
class={isMobile ? 'w-full' : 'max-w-md'}
33+
class="w-full md:max-w-md"
3734
/>
3835
{#if field.help || SETTING_CONFIG_INFO[field.key]}
3936
<p class="mt-1 text-xs text-muted-foreground">
@@ -50,7 +47,7 @@
5047
value={String(localConfig[field.key] ?? '')}
5148
onchange={(e) => onConfigChange(field.key, e.currentTarget.value)}
5249
placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`}
53-
class={isMobile ? 'min-h-[100px] w-full' : 'min-h-[100px] max-w-2xl'}
50+
class="min-h-[100px] w-full md:max-w-2xl"
5451
/>
5552
{#if field.help || SETTING_CONFIG_INFO[field.key]}
5653
<p class="mt-1 text-xs text-muted-foreground">
@@ -78,7 +75,7 @@
7875
}
7976
}}
8077
>
81-
<Select.Trigger class={isMobile ? 'w-full' : 'max-w-md'}>
78+
<Select.Trigger class="w-full md:w-auto md:max-w-md">
8279
<div class="flex items-center gap-2">
8380
{#if selectedOption?.icon}
8481
{@const IconComponent = selectedOption.icon}
Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script lang="ts">
22
import { Button } from '$lib/components/ui/button';
3+
import * as AlertDialog from '$lib/components/ui/alert-dialog';
34
45
interface Props {
56
onReset?: () => void;
@@ -8,8 +9,15 @@
89
910
let { onReset, onSave }: Props = $props();
1011
11-
function handleReset() {
12+
let showResetDialog = $state(false);
13+
14+
function handleResetClick() {
15+
showResetDialog = true;
16+
}
17+
18+
function handleConfirmReset() {
1219
onReset?.();
20+
showResetDialog = false;
1321
}
1422
1523
function handleSave() {
@@ -18,7 +26,23 @@
1826
</script>
1927

2028
<div class="flex justify-between border-t border-border/30 p-6">
21-
<Button variant="outline" onclick={handleReset}>Reset to default</Button>
29+
<Button variant="outline" onclick={handleResetClick}>Reset to default</Button>
2230

2331
<Button onclick={handleSave}>Save settings</Button>
2432
</div>
33+
34+
<AlertDialog.Root bind:open={showResetDialog}>
35+
<AlertDialog.Content>
36+
<AlertDialog.Header>
37+
<AlertDialog.Title>Reset Settings to Default</AlertDialog.Title>
38+
<AlertDialog.Description>
39+
Are you sure you want to reset all settings to their default values? This action cannot be
40+
undone and will permanently remove all your custom configurations.
41+
</AlertDialog.Description>
42+
</AlertDialog.Header>
43+
<AlertDialog.Footer>
44+
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
45+
<AlertDialog.Action onclick={handleConfirmReset}>Reset to Default</AlertDialog.Action>
46+
</AlertDialog.Footer>
47+
</AlertDialog.Content>
48+
</AlertDialog.Root>

tools/server/webui/src/lib/components/app/chat/ChatSidebar/ChatSidebar.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,15 @@
8787
<Sidebar.GroupContent>
8888
<Sidebar.Menu>
8989
{#each filteredConversations as conversation (conversation.id)}
90-
<Sidebar.MenuItem class="mb-1" onclick={handleMobileSidebarItemClick}>
90+
<Sidebar.MenuItem class="mb-1">
9191
<ChatSidebarConversationItem
9292
conversation={{
9393
id: conversation.id,
9494
name: conversation.name,
9595
lastModified: conversation.lastModified,
9696
currNode: conversation.currNode
9797
}}
98+
{handleMobileSidebarItemClick}
9899
isActive={currentChatId === conversation.id}
99100
onSelect={selectConversation}
100101
onEdit={editConversation}

tools/server/webui/src/lib/components/app/chat/ChatSidebar/ChatSidebarConversationItem.svelte

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
interface Props {
99
isActive?: boolean;
1010
conversation: DatabaseConversation;
11+
handleMobileSidebarItemClick?: () => void;
1112
onDelete?: (id: string) => void;
1213
onEdit?: (id: string, name: string) => void;
1314
onSelect?: (id: string) => void;
@@ -16,6 +17,7 @@
1617
1718
let {
1819
conversation,
20+
handleMobileSidebarItemClick,
1921
onDelete,
2022
onEdit,
2123
onSelect,
@@ -47,6 +49,7 @@
4749
4850
function handleConfirmEdit() {
4951
if (!editedName.trim()) return;
52+
showEditDialog = false;
5053
onEdit?.(conversation.id, editedName);
5154
}
5255
@@ -85,7 +88,12 @@
8588
: ''}"
8689
onclick={handleSelect}
8790
>
88-
<div class="text flex min-w-0 flex-1 items-center space-x-3">
91+
<!-- svelte-ignore a11y_click_events_have_key_events -->
92+
<!-- svelte-ignore a11y_no_static_element_interactions -->
93+
<div
94+
class="text flex min-w-0 flex-1 items-center space-x-3"
95+
onclick={handleMobileSidebarItemClick}
96+
>
8997
<div class="min-w-0 flex-1">
9098
<p class="truncate text-sm font-medium">{conversation.name}</p>
9199

@@ -178,5 +186,10 @@
178186
&:is(:hover) :global([data-slot='dropdown-menu-trigger']) {
179187
opacity: 1;
180188
}
189+
@media (max-width: 768px) {
190+
:global([data-slot='dropdown-menu-trigger']) {
191+
opacity: 1 !important;
192+
}
193+
}
181194
}
182195
</style>

tools/server/webui/src/lib/components/app/misc/ActionDropdown.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<DropdownMenu.Root bind:open>
3838
<DropdownMenu.Trigger
3939
class="flex h-6 w-6 cursor-pointer items-center justify-center rounded-md p-0 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground {triggerClass}"
40+
onclick={(e) => e.stopPropagation()}
4041
>
4142
{#if triggerTooltip}
4243
<Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
@@ -53,7 +54,7 @@
5354
{/if}
5455
</DropdownMenu.Trigger>
5556

56-
<DropdownMenu.Content {align} class="z-999 w-48">
57+
<DropdownMenu.Content {align} class="z-[999999] w-48">
5758
{#each actions as action, index (action.label)}
5859
{#if action.separator && index > 0}
5960
<DropdownMenu.Separator />

tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@
1919
bind:ref
2020
data-slot="alert-dialog-content"
2121
class={cn(
22-
'fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg',
22+
'fixed z-[999999] grid w-full gap-4 border bg-background p-6 shadow-lg duration-200',
23+
// Mobile: Bottom sheet behavior
24+
'right-0 bottom-0 left-0 max-h-[100dvh] translate-x-0 translate-y-0 overflow-y-auto rounded-t-lg',
25+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom-full',
26+
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-full',
27+
// Desktop: Centered dialog behavior
28+
'sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-h-[100vh] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-lg',
29+
'sm:data-[state=closed]:slide-out-to-bottom-0 sm:data-[state=closed]:zoom-out-95',
30+
'sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95',
2331
className
2432
)}
2533
{...restProps}

tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
<div
1414
bind:this={ref}
1515
data-slot="alert-dialog-footer"
16-
class={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
16+
class={cn(
17+
'mt-6 flex flex-row gap-2 sm:mt-0 sm:justify-end [&>*]:flex-1 sm:[&>*]:flex-none',
18+
className
19+
)}
1720
{...restProps}
1821
>
1922
{@render children?.()}

0 commit comments

Comments
 (0)