Skip to content

Commit

Permalink
add mobile menu (huggingface#64)
Browse files Browse the repository at this point in the history
* add mobile menu

* fix

* Auto-naming convos by summarizing them (huggingface#58)

* Auto-naming convos by summarizing them

* Ok let's do it only on first message then

* revamp

* location.reload for now

* avoid huge titles

* fix messages width

* add community feedback to nav

* fix tokens keeping coming even when changing conversation (huggingface#62)

* favicon

* 🐛 Fix generating bug (huggingface#68)

* 🐛 Fix redirect after delete

* ✨ Remove endoftext (huggingface#70)

* 🐛 Remove sanitized < (huggingface#71)

* 🩹 Change 301 to 302 in case we want to use the route for something else

* 🩹 Use passed fetch cc @julien-c

When using @huggingface/infernece in the backend we'll need to make it support custom fetch as well

* refactor mobile menu + improve accessibility

* fix missing menu on md size + regression share button on hover

* fix chat title truncate on mobile

* fix layout max-width on mobile

* use a single event dispatcher instead of 2

* add missing type="button"

* remove duplicated wrapper after merge conflict

---------

Co-authored-by: Victor Mustar <[email protected]>
Co-authored-by: Julien Chaumond <[email protected]>
Co-authored-by: Eliott C <[email protected]>
  • Loading branch information
4 people authored Apr 24, 2023
1 parent 229d4b4 commit 4dae10f
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 85 deletions.
60 changes: 60 additions & 0 deletions src/lib/components/MobileNav.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts">
import { navigating } from "$app/stores";
import { createEventDispatcher } from "svelte";
import { browser } from "$app/environment";
import { base } from "$app/paths";
import CarbonClose from "~icons/carbon/close";
import CarbonAdd from "~icons/carbon/add";
import CarbonTextAlignJustify from "~icons/carbon/text-align-justify";
export let isOpen = false;
export let title: string;
$: title = title || "New Chat";
let closeEl: HTMLButtonElement;
let openEl: HTMLButtonElement;
const dispatch = createEventDispatcher();
$: if ($navigating) {
dispatch("toggle", false);
}
$: if (isOpen && closeEl) {
closeEl.focus();
} else if (!isOpen && browser && document.activeElement === closeEl) {
openEl.focus();
}
</script>

<nav class="md:hidden flex items-center h-12 border-b px-4 justify-between dark:border-gray-800">
<button
type="button"
class="flex items-center justify-center w-9 h-9 -ml-3 shrink-0"
on:click={() => dispatch("toggle", true)}
aria-label="Open menu"
bind:this={openEl}><CarbonTextAlignJustify /></button
>
<span class="px-4 truncate">{title}</span>
<a href={base || "/"} class="flex items-center justify-center w-9 h-9 -mr-3 shrink-0"
><CarbonAdd /></a
>
</nav>
<nav
class="fixed inset-0 z-50 grid grid-rows-[auto,auto,1fr,auto] grid-cols-1 max-h-screen bg-white dark:bg-gray-900 bg-gradient-to-l from-gray-50 dark:from-gray-800/30 {isOpen
? 'block'
: 'hidden'}"
>
<div class="flex items-center px-4 h-12">
<button
type="button"
class="flex items-center justify-center ml-auto w-9 h-9 -mr-3"
on:click={() => dispatch("toggle", false)}
aria-label="Close menu"
bind:this={closeEl}><CarbonClose /></button
>
</div>
<slot />
</nav>
84 changes: 84 additions & 0 deletions src/lib/components/NavMenu.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script lang="ts">
import { base } from "$app/paths";
import { page } from "$app/stores";
import { createEventDispatcher } from "svelte";
import CarbonTrashCan from "~icons/carbon/trash-can";
import CarbonExport from "~icons/carbon/export";
import { switchTheme } from "$lib/switchTheme";
const dispatch = createEventDispatcher<{
shareConversation: { id: string; title: string };
deleteConversation: string;
}>();
export let conversations: Array<{
id: string;
title: string;
}> = [];
</script>

<div class="flex-none sticky top-0 p-3 flex flex-col">
<a
href={base || "/"}
class="border px-12 py-2.5 rounded-lg shadow bg-white dark:bg-gray-700 dark:border-gray-600 text-center"
>
New Chat
</a>
</div>
<div class="flex flex-col overflow-y-auto p-3 -mt-3 gap-1">
{#each conversations as conv}
<a
data-sveltekit-noscroll
href="{base}/conversation/{conv.id}"
class="group pl-3 pr-2 h-11 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center gap-1.5 {conv.id ===
$page.params.id
? 'bg-gray-100 dark:bg-gray-700'
: ''}"
>
<div class="flex-1 truncate">{conv.title}</div>

<button
type="button"
class="flex md:hidden md:group-hover:flex w-5 h-5 items-center justify-center rounded"
title="Share conversation"
on:click|preventDefault={() =>
dispatch("shareConversation", { id: conv.id, title: conv.title })}
>
<CarbonExport class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300 text-xs" />
</button>

<button
type="button"
class="flex md:hidden md:group-hover:flex w-5 h-5 items-center justify-center rounded"
title="Delete conversation"
on:click|preventDefault={() => dispatch("deleteConversation", conv.id)}
>
<CarbonTrashCan
class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300 text-xs"
/>
</button>
</a>
{/each}
</div>
<div class="flex flex-col p-3 gap-2">
<button
on:click={switchTheme}
type="button"
class="text-left flex items-center first-letter:capitalize truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
>
Theme
</button>
<a
href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions"
class="text-left flex items-center first-letter:capitalize truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
>
Community feedback
</a>
<a
href={base}
class="truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
>
Settings
</a>
</div>
7 changes: 1 addition & 6 deletions src/lib/components/chat/ChatWindow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@
const dispatch = createEventDispatcher<{ message: string; share: void }>();
</script>

<div class="relative h-screen">
<nav class="sm:hidden flex items-center h-12 border-b px-4 justify-between dark:border-gray-800">
<button><CarbonTextAlignJustify /></button>
<button>New Chat</button>
<button><CarbonAdd /></button>
</nav>
<div class="relative min-h-0">
<ChatMessages {loading} {pending} {messages} on:message />
<div
class="flex flex-col max-md:border-t dark:border-gray-800 items-center max-md:dark:bg-gray-900 max-md:bg-white bg-gradient-to-t from-white to-white/0 dark:from-gray-900 dark:to-gray-900/0 justify-center absolute inset-x-0 max-w-3xl xl:max-w-4xl mx-auto px-5 bottom-0 py-4 md:py-8 w-full"
Expand Down
10 changes: 10 additions & 0 deletions src/lib/switchTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function switchTheme() {
const { classList } = document.querySelector("html") as HTMLElement;
if (classList.contains("dark")) {
classList.remove("dark");
localStorage.theme = "light";
} else {
classList.add("dark");
localStorage.theme = "dark";
}
}
100 changes: 21 additions & 79 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,16 @@
import { page } from "$app/stores";
import "../styles/main.css";
import type { LayoutData } from "./$types";
import CarbonTrashCan from "~icons/carbon/trash-can";
import CarbonExport from "~icons/carbon/export";
import { base } from "$app/paths";
import { shareConversation } from "$lib/shareConversation";
import { UrlDependency } from "$lib/types/UrlDependency";
import MobileNav from "$lib/components/MobileNav.svelte";
import NavMenu from "$lib/components/NavMenu.svelte";
export let data: LayoutData;
function switchTheme() {
const { classList } = document.querySelector("html") as HTMLElement;
if (classList.contains("dark")) {
classList.remove("dark");
localStorage.theme = "light";
} else {
classList.add("dark");
localStorage.theme = "dark";
}
}
let isNavOpen = false;
async function deleteConversation(id: string) {
try {
Expand Down Expand Up @@ -50,76 +41,27 @@
</script>

<div
class="grid h-screen w-screen md:grid-cols-[280px,1fr] overflow-hidden text-smd dark:text-gray-300"
class="grid h-screen w-screen grid-cols-1 grid-rows-[auto,1fr] md:grid-rows-[1fr] md:grid-cols-[280px,1fr] overflow-hidden text-smd dark:text-gray-300"
>
<MobileNav
isOpen={isNavOpen}
on:toggle={(ev) => (isNavOpen = ev.detail)}
title={data.conversations.find((conv) => conv.id === $page.params.id)?.title}
>
<NavMenu
conversations={data.conversations}
on:shareConversation={(ev) => shareConversation(ev.detail.id, ev.detail.title)}
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
/>
</MobileNav>
<nav
class="max-md:hidden grid grid-rows-[auto,1fr,auto] grid-cols-1 max-h-screen bg-gradient-to-l from-gray-50 dark:from-gray-800/30 rounded-r-xl"
>
<div class="flex-none sticky top-0 p-3 flex flex-col">
<a
href={base || "/"}
class="border px-12 py-2.5 rounded-lg shadow bg-white dark:bg-gray-700 dark:border-gray-600 text-center"
>
New Chat
</a>
</div>
<div class="flex flex-col overflow-y-auto p-3 -mt-3 gap-1">
{#each data.conversations as conv}
<a
data-sveltekit-noscroll
href="{base}/conversation/{conv.id}"
class="pl-3 pr-2 h-11 group rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center gap-1.5 {conv.id ===
$page.params.id
? 'bg-gray-100 dark:bg-gray-700'
: ''}"
>
<div class="flex-1 truncate">{conv.title}</div>

<button
type="button"
class="w-5 h-5 items-center justify-center hidden group-hover:flex rounded"
title="Share conversation"
on:click|preventDefault={() => shareConversation(conv.id, conv.title)}
>
<CarbonExport
class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300 text-xs"
/>
</button>

<button
type="button"
class="w-5 h-5 items-center justify-center hidden group-hover:flex rounded"
title="Delete conversation"
on:click|preventDefault={() => deleteConversation(conv.id)}
>
<CarbonTrashCan
class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300 text-xs"
/>
</button>
</a>
{/each}
</div>
<div class="flex flex-col p-3 gap-2">
<button
on:click={switchTheme}
type="button"
class="text-left flex items-center first-letter:capitalize truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
>
Theme
</button>
<a
href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions"
class="text-left flex items-center first-letter:capitalize truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
>
Community feedback
</a>
<a
href={base}
class="truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
>
Settings
</a>
</div>
<NavMenu
conversations={data.conversations}
on:shareConversation={(ev) => shareConversation(ev.detail.id, ev.detail.title)}
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
/>
</nav>
<slot />
</div>

0 comments on commit 4dae10f

Please sign in to comment.