Spaces:
Running
Running
feat(nav): better mobile nav (#1725)
Browse files* feat: improve mobile settings navigation
* fix: make sure window resizing works & redirects properly
src/routes/settings/(nav)/+layout.svelte
CHANGED
@@ -8,10 +8,14 @@
|
|
8 |
import CarbonArrowUpRight from "~icons/carbon/ArrowUpRight";
|
9 |
import CarbonAdd from "~icons/carbon/add";
|
10 |
import CarbonTextLongParagraph from "~icons/carbon/text-long-paragraph";
|
|
|
11 |
|
12 |
import UserIcon from "~icons/carbon/user";
|
13 |
import type { LayoutData } from "../$types";
|
14 |
import { error } from "$lib/stores/errors";
|
|
|
|
|
|
|
15 |
|
16 |
interface Props {
|
17 |
data: LayoutData;
|
@@ -22,17 +26,39 @@
|
|
22 |
|
23 |
let previousPage: string = $state(base);
|
24 |
let assistantsSection: HTMLHeadingElement | undefined = $state();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
onMount(() => {
|
27 |
if (page.params?.assistantId && assistantsSection) {
|
28 |
assistantsSection.scrollIntoView();
|
29 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
});
|
31 |
|
32 |
afterNavigate(({ from }) => {
|
33 |
if (!from?.url.pathname.includes("settings")) {
|
34 |
previousPage = from?.url.toString() || previousPage;
|
35 |
}
|
|
|
|
|
|
|
|
|
36 |
});
|
37 |
|
38 |
const settings = useSettingsStore();
|
@@ -42,6 +68,18 @@
|
|
42 |
class="grid h-full w-full grid-cols-1 grid-rows-[auto,1fr] content-start gap-x-4 overflow-hidden p-4 md:grid-cols-3 md:grid-rows-[auto,1fr] md:p-8"
|
43 |
>
|
44 |
<div class="col-span-1 mb-4 flex items-center justify-between md:col-span-3">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
<h2 class="text-xl font-bold">Settings</h2>
|
46 |
<button
|
47 |
class="btn rounded-lg"
|
@@ -54,7 +92,8 @@
|
|
54 |
</button>
|
55 |
</div>
|
56 |
<div
|
57 |
-
class="col-span-1 flex flex-col overflow-y-auto whitespace-nowrap max-md:-mx-4 max-md:h-
|
|
|
58 |
>
|
59 |
<h3 class="pb-3 pl-3 pt-2 text-[.8rem] text-gray-800 sm:pl-1">Models</h3>
|
60 |
|
@@ -200,16 +239,17 @@
|
|
200 |
|
201 |
<div class="my-2 mt-auto w-full border-b border-gray-200"></div>
|
202 |
<a
|
203 |
-
href="{base}/settings"
|
204 |
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 max-md:order-first md:rounded-xl
|
205 |
-
{page.url.pathname === `${base}/settings` ? '!bg-gray-100 !text-gray-800' : ''}"
|
206 |
>
|
207 |
<UserIcon class="text-sm" />
|
208 |
Application Settings
|
209 |
</a>
|
210 |
</div>
|
211 |
<div
|
212 |
-
class="col-span-1 w-full overflow-y-auto overflow-x-clip px-1
|
|
|
213 |
>
|
214 |
{@render children?.()}
|
215 |
</div>
|
|
|
8 |
import CarbonArrowUpRight from "~icons/carbon/ArrowUpRight";
|
9 |
import CarbonAdd from "~icons/carbon/add";
|
10 |
import CarbonTextLongParagraph from "~icons/carbon/text-long-paragraph";
|
11 |
+
import CarbonChevronLeft from "~icons/carbon/chevron-left";
|
12 |
|
13 |
import UserIcon from "~icons/carbon/user";
|
14 |
import type { LayoutData } from "../$types";
|
15 |
import { error } from "$lib/stores/errors";
|
16 |
+
import { browser } from "$app/environment";
|
17 |
+
import { isDesktop } from "$lib/utils/isDesktop";
|
18 |
+
import { debounce } from "$lib/utils/debounce";
|
19 |
|
20 |
interface Props {
|
21 |
data: LayoutData;
|
|
|
26 |
|
27 |
let previousPage: string = $state(base);
|
28 |
let assistantsSection: HTMLHeadingElement | undefined = $state();
|
29 |
+
let showContent: boolean = $state(false);
|
30 |
+
|
31 |
+
function checkDesktopRedirect() {
|
32 |
+
if (browser && isDesktop(window) && page.url.pathname === `${base}/settings`) {
|
33 |
+
goto(`${base}/settings/application`);
|
34 |
+
}
|
35 |
+
}
|
36 |
|
37 |
onMount(() => {
|
38 |
if (page.params?.assistantId && assistantsSection) {
|
39 |
assistantsSection.scrollIntoView();
|
40 |
}
|
41 |
+
// Show content when not on the root settings page
|
42 |
+
showContent = page.url.pathname !== `${base}/settings`;
|
43 |
+
// Initial desktop redirect check
|
44 |
+
checkDesktopRedirect();
|
45 |
+
|
46 |
+
// Add resize listener for desktop redirect
|
47 |
+
if (browser) {
|
48 |
+
const debouncedCheck = debounce(checkDesktopRedirect, 100);
|
49 |
+
window.addEventListener("resize", debouncedCheck);
|
50 |
+
return () => window.removeEventListener("resize", debouncedCheck);
|
51 |
+
}
|
52 |
});
|
53 |
|
54 |
afterNavigate(({ from }) => {
|
55 |
if (!from?.url.pathname.includes("settings")) {
|
56 |
previousPage = from?.url.toString() || previousPage;
|
57 |
}
|
58 |
+
// Show content when not on the root settings page
|
59 |
+
showContent = page.url.pathname !== `${base}/settings`;
|
60 |
+
// Check desktop redirect after navigation
|
61 |
+
checkDesktopRedirect();
|
62 |
});
|
63 |
|
64 |
const settings = useSettingsStore();
|
|
|
68 |
class="grid h-full w-full grid-cols-1 grid-rows-[auto,1fr] content-start gap-x-4 overflow-hidden p-4 md:grid-cols-3 md:grid-rows-[auto,1fr] md:p-8"
|
69 |
>
|
70 |
<div class="col-span-1 mb-4 flex items-center justify-between md:col-span-3">
|
71 |
+
{#if showContent && browser}
|
72 |
+
<button
|
73 |
+
class="btn rounded-lg md:hidden"
|
74 |
+
aria-label="Back to menu"
|
75 |
+
onclick={() => {
|
76 |
+
showContent = false;
|
77 |
+
goto(`${base}/settings`);
|
78 |
+
}}
|
79 |
+
>
|
80 |
+
<CarbonChevronLeft class="text-xl text-gray-900 hover:text-black" />
|
81 |
+
</button>
|
82 |
+
{/if}
|
83 |
<h2 class="text-xl font-bold">Settings</h2>
|
84 |
<button
|
85 |
class="btn rounded-lg"
|
|
|
92 |
</button>
|
93 |
</div>
|
94 |
<div
|
95 |
+
class="col-span-1 flex flex-col overflow-y-auto whitespace-nowrap max-md:-mx-4 max-md:h-full md:pr-6"
|
96 |
+
class:max-md:hidden={showContent && browser}
|
97 |
>
|
98 |
<h3 class="pb-3 pl-3 pt-2 text-[.8rem] text-gray-800 sm:pl-1">Models</h3>
|
99 |
|
|
|
239 |
|
240 |
<div class="my-2 mt-auto w-full border-b border-gray-200"></div>
|
241 |
<a
|
242 |
+
href="{base}/settings/application"
|
243 |
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 max-md:order-first md:rounded-xl
|
244 |
+
{page.url.pathname === `${base}/settings/application` ? '!bg-gray-100 !text-gray-800' : ''}"
|
245 |
>
|
246 |
<UserIcon class="text-sm" />
|
247 |
Application Settings
|
248 |
</a>
|
249 |
</div>
|
250 |
<div
|
251 |
+
class="col-span-1 w-full overflow-y-auto overflow-x-clip px-1 md:col-span-2 md:row-span-2"
|
252 |
+
class:max-md:hidden={!showContent && browser}
|
253 |
>
|
254 |
{@render children?.()}
|
255 |
</div>
|
src/routes/settings/(nav)/+layout.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
export const ssr = false;
|
src/routes/settings/(nav)/+page.svelte
CHANGED
@@ -1,105 +0,0 @@
|
|
1 |
-
<script lang="ts">
|
2 |
-
import CarbonTrashCan from "~icons/carbon/trash-can";
|
3 |
-
import CarbonArrowUpRight from "~icons/carbon/arrow-up-right";
|
4 |
-
|
5 |
-
import { useSettingsStore } from "$lib/stores/settings";
|
6 |
-
import Switch from "$lib/components/Switch.svelte";
|
7 |
-
import { env as envPublic } from "$env/dynamic/public";
|
8 |
-
import { goto } from "$app/navigation";
|
9 |
-
import { error } from "$lib/stores/errors";
|
10 |
-
import { base } from "$app/paths";
|
11 |
-
|
12 |
-
let settings = useSettingsStore();
|
13 |
-
</script>
|
14 |
-
|
15 |
-
<div class="flex w-full flex-col gap-5">
|
16 |
-
<div class="flex flex-col items-start justify-between text-xl font-semibold text-gray-800">
|
17 |
-
<h2>Application Settings</h2>
|
18 |
-
{#if !!envPublic.PUBLIC_COMMIT_SHA}
|
19 |
-
<a
|
20 |
-
href={`https://github.com/huggingface/chat-ui/commit/${envPublic.PUBLIC_COMMIT_SHA}`}
|
21 |
-
target="_blank"
|
22 |
-
rel="noreferrer"
|
23 |
-
class="text-sm font-light text-gray-500"
|
24 |
-
>
|
25 |
-
Latest deployment <span class="gap-2 font-mono"
|
26 |
-
>{envPublic.PUBLIC_COMMIT_SHA.slice(0, 7)}</span
|
27 |
-
>
|
28 |
-
</a>
|
29 |
-
{/if}
|
30 |
-
</div>
|
31 |
-
<div class="flex h-full max-w-2xl flex-col gap-2 max-sm:pt-0">
|
32 |
-
{#if envPublic.PUBLIC_APP_DATA_SHARING === "1"}
|
33 |
-
<label class="flex items-center">
|
34 |
-
<Switch
|
35 |
-
name="shareConversationsWithModelAuthors"
|
36 |
-
bind:checked={$settings.shareConversationsWithModelAuthors}
|
37 |
-
/>
|
38 |
-
<div class="inline cursor-pointer select-none items-center gap-2 pl-2">
|
39 |
-
Share conversations with model authors
|
40 |
-
</div>
|
41 |
-
</label>
|
42 |
-
|
43 |
-
<p class="text-sm text-gray-500">
|
44 |
-
Sharing your data will help improve the training data and make open models better over time.
|
45 |
-
</p>
|
46 |
-
{/if}
|
47 |
-
<label class="mt-6 flex items-center">
|
48 |
-
<Switch name="hideEmojiOnSidebar" bind:checked={$settings.hideEmojiOnSidebar} />
|
49 |
-
<div class="inline cursor-pointer select-none items-center gap-2 pl-2 font-semibold">
|
50 |
-
Hide emoticons in conversation topics
|
51 |
-
<p class="text-sm font-normal text-gray-500">
|
52 |
-
Emoticons are shown in the sidebar by default, enable this to hide them.
|
53 |
-
</p>
|
54 |
-
</div>
|
55 |
-
</label>
|
56 |
-
|
57 |
-
<label class="mt-6 flex items-center">
|
58 |
-
<Switch name="disableStream" bind:checked={$settings.disableStream} />
|
59 |
-
<div class="inline cursor-pointer select-none items-center gap-2 pl-2 font-semibold">
|
60 |
-
Disable streaming tokens
|
61 |
-
</div>
|
62 |
-
</label>
|
63 |
-
|
64 |
-
<label class="mt-6 flex items-center">
|
65 |
-
<Switch name="directPaste" bind:checked={$settings.directPaste} />
|
66 |
-
<div class="inline cursor-pointer select-none items-center gap-2 pl-2 font-semibold">
|
67 |
-
Paste text directly into chat
|
68 |
-
<p class="text-sm font-normal text-gray-500">
|
69 |
-
By default, when pasting long text into the chat, we treat it as a plaintext file. Enable
|
70 |
-
this to paste directly into the chat instead.
|
71 |
-
</p>
|
72 |
-
</div>
|
73 |
-
</label>
|
74 |
-
|
75 |
-
<div class="mt-12 flex flex-col gap-3">
|
76 |
-
<a
|
77 |
-
href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions"
|
78 |
-
target="_blank"
|
79 |
-
rel="noreferrer"
|
80 |
-
class="flex items-center underline decoration-gray-300 underline-offset-2 hover:decoration-gray-700"
|
81 |
-
><CarbonArrowUpRight class="mr-1.5 shrink-0 text-sm " /> Share your feedback on HuggingChat</a
|
82 |
-
>
|
83 |
-
<button
|
84 |
-
onclick={async (e) => {
|
85 |
-
e.preventDefault();
|
86 |
-
|
87 |
-
confirm("Are you sure you want to delete all conversations?") &&
|
88 |
-
(await fetch(`${base}/api/conversations`, {
|
89 |
-
method: "DELETE",
|
90 |
-
})
|
91 |
-
.then(async () => {
|
92 |
-
await goto(`${base}/`, { invalidateAll: true });
|
93 |
-
})
|
94 |
-
.catch((err) => {
|
95 |
-
console.error(err);
|
96 |
-
$error = err.message;
|
97 |
-
}));
|
98 |
-
}}
|
99 |
-
type="submit"
|
100 |
-
class="flex items-center underline decoration-gray-300 underline-offset-2 hover:decoration-gray-700"
|
101 |
-
><CarbonTrashCan class="mr-2 inline text-sm text-red-500" />Delete all conversations</button
|
102 |
-
>
|
103 |
-
</div>
|
104 |
-
</div>
|
105 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/routes/settings/(nav)/application/+page.svelte
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import CarbonTrashCan from "~icons/carbon/trash-can";
|
3 |
+
import CarbonArrowUpRight from "~icons/carbon/arrow-up-right";
|
4 |
+
|
5 |
+
import { useSettingsStore } from "$lib/stores/settings";
|
6 |
+
import Switch from "$lib/components/Switch.svelte";
|
7 |
+
import { env as envPublic } from "$env/dynamic/public";
|
8 |
+
import { goto } from "$app/navigation";
|
9 |
+
import { error } from "$lib/stores/errors";
|
10 |
+
import { base } from "$app/paths";
|
11 |
+
|
12 |
+
let settings = useSettingsStore();
|
13 |
+
</script>
|
14 |
+
|
15 |
+
<div class="flex w-full flex-col gap-5">
|
16 |
+
<div class="flex flex-col items-start justify-between text-xl font-semibold text-gray-800">
|
17 |
+
<h2>Application Settings</h2>
|
18 |
+
{#if !!envPublic.PUBLIC_COMMIT_SHA}
|
19 |
+
<a
|
20 |
+
href={`https://github.com/huggingface/chat-ui/commit/${envPublic.PUBLIC_COMMIT_SHA}`}
|
21 |
+
target="_blank"
|
22 |
+
rel="noreferrer"
|
23 |
+
class="text-sm font-light text-gray-500"
|
24 |
+
>
|
25 |
+
Latest deployment <span class="gap-2 font-mono"
|
26 |
+
>{envPublic.PUBLIC_COMMIT_SHA.slice(0, 7)}</span
|
27 |
+
>
|
28 |
+
</a>
|
29 |
+
{/if}
|
30 |
+
</div>
|
31 |
+
<div class="flex h-full max-w-2xl flex-col gap-2 max-sm:pt-0">
|
32 |
+
{#if envPublic.PUBLIC_APP_DATA_SHARING === "1"}
|
33 |
+
<label class="flex items-center">
|
34 |
+
<Switch
|
35 |
+
name="shareConversationsWithModelAuthors"
|
36 |
+
bind:checked={$settings.shareConversationsWithModelAuthors}
|
37 |
+
/>
|
38 |
+
<div class="inline cursor-pointer select-none items-center gap-2 pl-2">
|
39 |
+
Share conversations with model authors
|
40 |
+
</div>
|
41 |
+
</label>
|
42 |
+
|
43 |
+
<p class="text-sm text-gray-500">
|
44 |
+
Sharing your data will help improve the training data and make open models better over time.
|
45 |
+
</p>
|
46 |
+
{/if}
|
47 |
+
<label class="mt-6 flex items-center">
|
48 |
+
<Switch name="hideEmojiOnSidebar" bind:checked={$settings.hideEmojiOnSidebar} />
|
49 |
+
<div class="inline cursor-pointer select-none items-center gap-2 pl-2 font-semibold">
|
50 |
+
Hide emoticons in conversation topics
|
51 |
+
<p class="text-sm font-normal text-gray-500">
|
52 |
+
Emoticons are shown in the sidebar by default, enable this to hide them.
|
53 |
+
</p>
|
54 |
+
</div>
|
55 |
+
</label>
|
56 |
+
|
57 |
+
<label class="mt-6 flex items-center">
|
58 |
+
<Switch name="disableStream" bind:checked={$settings.disableStream} />
|
59 |
+
<div class="inline cursor-pointer select-none items-center gap-2 pl-2 font-semibold">
|
60 |
+
Disable streaming tokens
|
61 |
+
</div>
|
62 |
+
</label>
|
63 |
+
|
64 |
+
<label class="mt-6 flex items-center">
|
65 |
+
<Switch name="directPaste" bind:checked={$settings.directPaste} />
|
66 |
+
<div class="inline cursor-pointer select-none items-center gap-2 pl-2 font-semibold">
|
67 |
+
Paste text directly into chat
|
68 |
+
<p class="text-sm font-normal text-gray-500">
|
69 |
+
By default, when pasting long text into the chat, we treat it as a plaintext file. Enable
|
70 |
+
this to paste directly into the chat instead.
|
71 |
+
</p>
|
72 |
+
</div>
|
73 |
+
</label>
|
74 |
+
|
75 |
+
<div class="mt-12 flex flex-col gap-3">
|
76 |
+
<a
|
77 |
+
href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions"
|
78 |
+
target="_blank"
|
79 |
+
rel="noreferrer"
|
80 |
+
class="flex items-center underline decoration-gray-300 underline-offset-2 hover:decoration-gray-700"
|
81 |
+
><CarbonArrowUpRight class="mr-1.5 shrink-0 text-sm " /> Share your feedback on HuggingChat</a
|
82 |
+
>
|
83 |
+
<button
|
84 |
+
onclick={async (e) => {
|
85 |
+
e.preventDefault();
|
86 |
+
|
87 |
+
confirm("Are you sure you want to delete all conversations?") &&
|
88 |
+
(await fetch(`${base}/api/conversations`, {
|
89 |
+
method: "DELETE",
|
90 |
+
})
|
91 |
+
.then(async () => {
|
92 |
+
await goto(`${base}/`, { invalidateAll: true });
|
93 |
+
})
|
94 |
+
.catch((err) => {
|
95 |
+
console.error(err);
|
96 |
+
$error = err.message;
|
97 |
+
}));
|
98 |
+
}}
|
99 |
+
type="submit"
|
100 |
+
class="flex items-center underline decoration-gray-300 underline-offset-2 hover:decoration-gray-700"
|
101 |
+
><CarbonTrashCan class="mr-2 inline text-sm text-red-500" />Delete all conversations</button
|
102 |
+
>
|
103 |
+
</div>
|
104 |
+
</div>
|
105 |
+
</div>
|