improve mobile
This commit is contained in:
parent
c2cc437061
commit
b468afdc8c
10 changed files with 78 additions and 31 deletions
|
|
@ -5,6 +5,7 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently -n \"convex,vite\" -c \"blue.bold,green.bold\" \"convex dev\" \"vite dev\"",
|
||||
"dev:host": "concurrently -n \"convex,vite\" -c \"blue.bold,green.bold\" \"convex dev\" \"vite dev --host\"",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
|
|
|
|||
29
src/app.css
29
src/app.css
|
|
@ -54,7 +54,7 @@
|
|||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.2409 0.0201 307.5346);
|
||||
--background: oklch(0.2409 0.0201 267.5346);
|
||||
--foreground: oklch(0.8398 0.0387 309.5391);
|
||||
--card: oklch(0.2803 0.0232 307.5413);
|
||||
--card-foreground: oklch(0.8456 0.0302 341.4597);
|
||||
|
|
@ -211,13 +211,38 @@
|
|||
--shadow-2xl: var(--shadow-2xl);
|
||||
}
|
||||
|
||||
@utility fill-device {
|
||||
height: 100vh;
|
||||
height: 100dvh; /* Dynamic viewport height - newer browsers */
|
||||
|
||||
/* Alternative: Use env() for safe areas */
|
||||
min-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
|
||||
|
||||
/* Ensure full width */
|
||||
width: 100vw;
|
||||
width: 100dvw; /* Dynamic viewport width */
|
||||
|
||||
/* Remove default margins/padding */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@utility fill-device-height {
|
||||
height: 100svh;
|
||||
|
||||
/* min-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom)); */
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground bg-noise;
|
||||
@apply bg-background text-foreground bg-noise fill-device;
|
||||
position: fixed;
|
||||
overflow: clip;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content"
|
||||
/>
|
||||
<title>thom.chat</title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
let { class: className, children, ...rest }: HTMLAttributes<HTMLDivElement> = $props();
|
||||
</script>
|
||||
|
||||
<div {...rest} class={cn('bg-background bg-noise col-start-2 h-screen', className)}>
|
||||
<div {...rest} class={cn('bg-background bg-noise fill-device-height col-start-2', className)}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<div
|
||||
{...rest}
|
||||
class={cn(
|
||||
'bg-sidebar border-sidebar-border col-start-1 h-screen w-[--sidebar-width] border-r',
|
||||
'bg-sidebar border-sidebar-border fill-device-height col-start-1 w-[--sidebar-width] border-r',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
import { callGenerateMessage } from '../api/generate-message/call.js';
|
||||
import ModelPicker from './model-picker.svelte';
|
||||
import SearchModal from './search-modal.svelte';
|
||||
import { shortcut } from '$lib/actions/shortcut.svelte.js';
|
||||
import { mergeAttrs } from 'melt';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
|
|
@ -378,9 +380,13 @@
|
|||
<title>Chat | thom.chat</title>
|
||||
</svelte:head>
|
||||
|
||||
<svelte:window
|
||||
use:shortcut={[{ ctrl: true, key: 'd', callback: () => scrollState.scrollToBottom() }]}
|
||||
/>
|
||||
|
||||
<Sidebar.Root
|
||||
bind:open={sidebarOpen}
|
||||
class="h-screen overflow-clip"
|
||||
class="fill-device-height overflow-clip"
|
||||
{...currentModelSupportsImages ? omit(fileUpload.dropzone, ['onclick']) : {}}
|
||||
>
|
||||
<AppSidebar bind:searchModalOpen />
|
||||
|
|
@ -443,7 +449,7 @@
|
|||
<LightSwitch variant="ghost" class="size-8" />
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div bind:this={conversationList} class="h-screen overflow-y-auto">
|
||||
<div bind:this={conversationList} class="fill-device-height overflow-y-auto">
|
||||
<div
|
||||
class={cn('mx-auto flex max-w-3xl flex-col', {
|
||||
'pt-10': page.url.pathname !== '/chat',
|
||||
|
|
@ -452,6 +458,8 @@
|
|||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
<Tooltip placement="top">
|
||||
{#snippet trigger(tooltip)}
|
||||
<Button
|
||||
onclick={() => scrollState.scrollToBottom()}
|
||||
variant="secondary"
|
||||
|
|
@ -460,11 +468,16 @@
|
|||
'text-muted-foreground !border-border absolute bottom-0 left-1/2 z-10 -translate-x-1/2 rounded-full !border !pl-3 text-xs transition',
|
||||
notAtBottom.current ? 'opacity-100' : 'pointer-events-none scale-95 opacity-0',
|
||||
]}
|
||||
style="bottom: {wrapperSize.height + 5}px;"
|
||||
{...mergeAttrs(tooltip.trigger, {
|
||||
style: `bottom: ${wrapperSize.height + 5}px;`,
|
||||
})}
|
||||
>
|
||||
Scroll to bottom
|
||||
<ChevronDownIcon class="inline" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
{cmdOrCtrl} + D
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
@ -572,10 +585,10 @@
|
|||
{...pick(popover.trigger, ['id', 'style', 'onfocusout', 'onfocus'])}
|
||||
bind:this={textarea}
|
||||
disabled={textareaDisabled}
|
||||
class="text-foreground placeholder:text-muted-foreground/60 max-h-64 min-h-[80px] w-full resize-none !overflow-y-auto bg-transparent px-3 text-base leading-6 outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||
class="text-foreground placeholder:text-muted-foreground/60 max-h-64 min-h-[60px] w-full resize-none !overflow-y-auto bg-transparent px-3 text-base leading-6 outline-none disabled:cursor-not-allowed disabled:opacity-50 sm:min-h-[80px]"
|
||||
placeholder={isGenerating
|
||||
? 'Generating response...'
|
||||
: 'Type your message here... Tag rules with @'}
|
||||
: 'Type your message here, tag rules with @'}
|
||||
name="message"
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey && !popover.open) {
|
||||
|
|
@ -634,23 +647,23 @@
|
|||
{isGenerating ? 'Stop generation' : 'Send message'}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="flex flex-col items-start gap-2 pr-2 sm:flex-row sm:items-center">
|
||||
<div class="flex flex-row flex-wrap items-center gap-2 pr-2">
|
||||
<ModelPicker onlyImageModels={selectedImages.length > 0} />
|
||||
<button
|
||||
type="button"
|
||||
class={cn(
|
||||
'border-border flex items-center gap-1 rounded-full border px-2 py-1 text-xs transition-colors',
|
||||
'border-border flex items-center gap-1 rounded-full border px-1 py-1 text-xs transition-colors sm:px-2',
|
||||
settings.webSearchEnabled ? 'bg-accent/50' : 'hover:bg-accent/20'
|
||||
)}
|
||||
onclick={() => (settings.webSearchEnabled = !settings.webSearchEnabled)}
|
||||
>
|
||||
<SearchIcon class="!size-3" />
|
||||
<span class="whitespace-nowrap">Web search</span>
|
||||
<span class="hidden whitespace-nowrap sm:inline">Web search</span>
|
||||
</button>
|
||||
{#if currentModelSupportsImages}
|
||||
<button
|
||||
type="button"
|
||||
class="border-border hover:bg-accent/20 flex items-center gap-1 rounded-full border px-2 py-1 text-xs transition-colors disabled:opacity-50"
|
||||
class="border-border hover:bg-accent/20 flex items-center gap-1 rounded-full border px-1 py-1 text-xs transition-colors disabled:opacity-50 sm:px-2"
|
||||
onclick={() => fileInput?.click()}
|
||||
disabled={isUploading}
|
||||
>
|
||||
|
|
@ -661,7 +674,7 @@
|
|||
{:else}
|
||||
<ImageIcon class="!size-3" />
|
||||
{/if}
|
||||
<span class="whitespace-nowrap">Attach image</span>
|
||||
<span class="hidden whitespace-nowrap sm:inline">Attach image</span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@
|
|||
<button
|
||||
{...popover.trigger}
|
||||
class={cn(
|
||||
'ring-offset-background focus:ring-ring flex w-full items-center justify-between rounded-lg px-2 py-1 text-xs transition hover:text-white focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'ring-offset-background focus:ring-ring flex items-center justify-between rounded-lg px-2 py-1 text-xs transition hover:text-white focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
aria-expanded={open}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<svelte:window use:shortcut={{ ctrl: true, key: 'k', callback: () => (open = true) }} />
|
||||
<svelte:window use:shortcut={[{ ctrl: true, key: 'k', callback: () => (open = true) }]} />
|
||||
|
||||
<Modal bind:open>
|
||||
<div class="space-y-4">
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
</div>
|
||||
{:else if search.data?.length}
|
||||
<div class="max-h-96 space-y-2 overflow-y-auto">
|
||||
{#each search.data as { conversation, messages, score, titleMatch }, index}
|
||||
{#each search.data as { conversation, messages, titleMatch }, index}
|
||||
<div
|
||||
data-result-index={index}
|
||||
class="border-border flex cursor-pointer items-center justify-between gap-2 rounded-lg border px-3 py-2 transition-colors {index ===
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
<meta name="description" content="A shared conversation from thom.chat" />
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen">
|
||||
<div class="fill-device-height">
|
||||
<!-- Header -->
|
||||
<header
|
||||
class="border-border bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 border-b backdrop-blur"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
|||
import Icons from 'unplugin-icons/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
|
|
@ -37,4 +39,7 @@ export default defineConfig({
|
|||
},
|
||||
],
|
||||
},
|
||||
server: {
|
||||
allowedHosts: isDev ? true : undefined,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue