Disable the share button while generating (#1958)
Browse files
src/lib/components/MobileNav.svelte
CHANGED
|
@@ -16,6 +16,7 @@
|
|
| 16 |
import IconBurger from "$lib/components/icons/IconBurger.svelte";
|
| 17 |
import { Spring } from "svelte/motion";
|
| 18 |
import { shareModal } from "$lib/stores/shareModal";
|
|
|
|
| 19 |
interface Props {
|
| 20 |
title: string | undefined;
|
| 21 |
children?: import("svelte").Snippet;
|
|
@@ -28,7 +29,10 @@
|
|
| 28 |
|
| 29 |
const isHuggingChat = $derived(Boolean(page.data?.publicConfig?.isHuggingChat));
|
| 30 |
const canShare = $derived(
|
| 31 |
-
isHuggingChat &&
|
|
|
|
|
|
|
|
|
|
| 32 |
);
|
| 33 |
|
| 34 |
// Define the width for the drawer (less than 100% to create the gap)
|
|
|
|
| 16 |
import IconBurger from "$lib/components/icons/IconBurger.svelte";
|
| 17 |
import { Spring } from "svelte/motion";
|
| 18 |
import { shareModal } from "$lib/stores/shareModal";
|
| 19 |
+
import { loading } from "$lib/stores/loading";
|
| 20 |
interface Props {
|
| 21 |
title: string | undefined;
|
| 22 |
children?: import("svelte").Snippet;
|
|
|
|
| 29 |
|
| 30 |
const isHuggingChat = $derived(Boolean(page.data?.publicConfig?.isHuggingChat));
|
| 31 |
const canShare = $derived(
|
| 32 |
+
isHuggingChat &&
|
| 33 |
+
!$loading &&
|
| 34 |
+
Boolean(page.params?.id) &&
|
| 35 |
+
page.route.id?.startsWith("/conversation/")
|
| 36 |
);
|
| 37 |
|
| 38 |
// Define the width for the drawer (less than 100% to create the gap)
|
src/lib/stores/loading.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { writable } from "svelte/store";
|
| 2 |
+
|
| 3 |
+
export const loading = writable(false);
|
src/routes/+layout.svelte
CHANGED
|
@@ -8,6 +8,7 @@
|
|
| 8 |
|
| 9 |
import { error } from "$lib/stores/errors";
|
| 10 |
import { createSettingsStore } from "$lib/stores/settings";
|
|
|
|
| 11 |
|
| 12 |
import Toast from "$lib/components/Toast.svelte";
|
| 13 |
import NavMenu from "$lib/components/NavMenu.svelte";
|
|
@@ -244,9 +245,11 @@
|
|
| 244 |
{#if canShare}
|
| 245 |
<button
|
| 246 |
type="button"
|
| 247 |
-
class="hidden size-8 items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white/90 text-sm font-medium text-gray-700 shadow-sm hover:bg-white/60 hover:text-gray-500 dark:border-gray-700 dark:bg-gray-800/80 dark:text-gray-200 dark:hover:bg-gray-700 md:absolute md:right-6 md:top-5 md:flex
|
|
|
|
| 248 |
onclick={() => shareModal.open()}
|
| 249 |
aria-label="Share conversation"
|
|
|
|
| 250 |
>
|
| 251 |
<IconShare />
|
| 252 |
</button>
|
|
|
|
| 8 |
|
| 9 |
import { error } from "$lib/stores/errors";
|
| 10 |
import { createSettingsStore } from "$lib/stores/settings";
|
| 11 |
+
import { loading } from "$lib/stores/loading";
|
| 12 |
|
| 13 |
import Toast from "$lib/components/Toast.svelte";
|
| 14 |
import NavMenu from "$lib/components/NavMenu.svelte";
|
|
|
|
| 245 |
{#if canShare}
|
| 246 |
<button
|
| 247 |
type="button"
|
| 248 |
+
class="hidden size-8 items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white/90 text-sm font-medium text-gray-700 shadow-sm hover:bg-white/60 hover:text-gray-500 dark:border-gray-700 dark:bg-gray-800/80 dark:text-gray-200 dark:hover:bg-gray-700 md:absolute md:right-6 md:top-5 md:flex
|
| 249 |
+
{$loading ? 'cursor-not-allowed opacity-40' : ''}"
|
| 250 |
onclick={() => shareModal.open()}
|
| 251 |
aria-label="Share conversation"
|
| 252 |
+
disabled={$loading}
|
| 253 |
>
|
| 254 |
<IconShare />
|
| 255 |
</button>
|
src/routes/+page.svelte
CHANGED
|
@@ -13,11 +13,11 @@
|
|
| 13 |
import { findCurrentModel } from "$lib/utils/models";
|
| 14 |
import { sanitizeUrlParam } from "$lib/utils/urlParams";
|
| 15 |
import { onMount, tick } from "svelte";
|
|
|
|
| 16 |
|
| 17 |
let { data } = $props();
|
| 18 |
|
| 19 |
let hasModels = $derived(Boolean(data.models?.length));
|
| 20 |
-
let loading = $state(false);
|
| 21 |
let files: File[] = $state([]);
|
| 22 |
let draft = $state("");
|
| 23 |
|
|
@@ -25,7 +25,7 @@
|
|
| 25 |
|
| 26 |
async function createConversation(message: string) {
|
| 27 |
try {
|
| 28 |
-
loading = true;
|
| 29 |
|
| 30 |
// check if $settings.activeModel is a valid model
|
| 31 |
// else check if it's an assistant, and use that model
|
|
@@ -71,7 +71,7 @@
|
|
| 71 |
error.set((err as Error).message || ERROR_MESSAGES.default);
|
| 72 |
console.error(err);
|
| 73 |
} finally {
|
| 74 |
-
loading = false;
|
| 75 |
}
|
| 76 |
}
|
| 77 |
|
|
@@ -112,7 +112,7 @@
|
|
| 112 |
{#if hasModels}
|
| 113 |
<ChatWindow
|
| 114 |
onmessage={(message) => createConversation(message)}
|
| 115 |
-
{loading}
|
| 116 |
{currentModel}
|
| 117 |
models={data.models}
|
| 118 |
bind:files
|
|
|
|
| 13 |
import { findCurrentModel } from "$lib/utils/models";
|
| 14 |
import { sanitizeUrlParam } from "$lib/utils/urlParams";
|
| 15 |
import { onMount, tick } from "svelte";
|
| 16 |
+
import { loading } from "$lib/stores/loading.js";
|
| 17 |
|
| 18 |
let { data } = $props();
|
| 19 |
|
| 20 |
let hasModels = $derived(Boolean(data.models?.length));
|
|
|
|
| 21 |
let files: File[] = $state([]);
|
| 22 |
let draft = $state("");
|
| 23 |
|
|
|
|
| 25 |
|
| 26 |
async function createConversation(message: string) {
|
| 27 |
try {
|
| 28 |
+
$loading = true;
|
| 29 |
|
| 30 |
// check if $settings.activeModel is a valid model
|
| 31 |
// else check if it's an assistant, and use that model
|
|
|
|
| 71 |
error.set((err as Error).message || ERROR_MESSAGES.default);
|
| 72 |
console.error(err);
|
| 73 |
} finally {
|
| 74 |
+
$loading = false;
|
| 75 |
}
|
| 76 |
}
|
| 77 |
|
|
|
|
| 112 |
{#if hasModels}
|
| 113 |
<ChatWindow
|
| 114 |
onmessage={(message) => createConversation(message)}
|
| 115 |
+
loading={$loading}
|
| 116 |
{currentModel}
|
| 117 |
models={data.models}
|
| 118 |
bind:files
|
src/routes/conversation/[id]/+page.svelte
CHANGED
|
@@ -26,10 +26,10 @@
|
|
| 26 |
import "katex/dist/katex.min.css";
|
| 27 |
import { updateDebouncer } from "$lib/utils/updates.js";
|
| 28 |
import SubscribeModal from "$lib/components/SubscribeModal.svelte";
|
|
|
|
| 29 |
|
| 30 |
let { data = $bindable() } = $props();
|
| 31 |
|
| 32 |
-
let loading = $state(false);
|
| 33 |
let pending = $state(false);
|
| 34 |
let initialRun = true;
|
| 35 |
let showSubscribeModal = $state(false);
|
|
@@ -96,7 +96,7 @@
|
|
| 96 |
|
| 97 |
async function convFromShared() {
|
| 98 |
try {
|
| 99 |
-
loading = true;
|
| 100 |
const res = await fetch(`${base}/conversation`, {
|
| 101 |
method: "POST",
|
| 102 |
headers: {
|
|
@@ -135,7 +135,7 @@
|
|
| 135 |
}): Promise<void> {
|
| 136 |
try {
|
| 137 |
$isAborted = false;
|
| 138 |
-
loading = true;
|
| 139 |
pending = true;
|
| 140 |
const base64Files = await Promise.all(
|
| 141 |
(files ?? []).map((file) =>
|
|
@@ -335,7 +335,7 @@
|
|
| 335 |
}
|
| 336 |
console.error(err);
|
| 337 |
} finally {
|
| 338 |
-
loading = false;
|
| 339 |
pending = false;
|
| 340 |
await invalidateAll();
|
| 341 |
}
|
|
@@ -348,11 +348,11 @@
|
|
| 348 |
if (r.ok) {
|
| 349 |
setTimeout(() => {
|
| 350 |
$isAborted = true;
|
| 351 |
-
loading = false;
|
| 352 |
}, 500);
|
| 353 |
} else {
|
| 354 |
$isAborted = true;
|
| 355 |
-
loading = false;
|
| 356 |
}
|
| 357 |
});
|
| 358 |
}
|
|
@@ -375,7 +375,7 @@
|
|
| 375 |
const streaming = isConversationStreaming(messages);
|
| 376 |
if (streaming) {
|
| 377 |
addBackgroundGeneration({ id: page.params.id, startedAt: Date.now() });
|
| 378 |
-
loading = true;
|
| 379 |
}
|
| 380 |
});
|
| 381 |
|
|
@@ -388,7 +388,7 @@
|
|
| 388 |
await goto(`${base}/conversation/${convId}`, { invalidateAll: true });
|
| 389 |
})
|
| 390 |
.then(async () => await writeMessage({ prompt: content }))
|
| 391 |
-
.finally(() => (loading = false));
|
| 392 |
}
|
| 393 |
}
|
| 394 |
|
|
@@ -415,7 +415,7 @@
|
|
| 415 |
isRetry: true,
|
| 416 |
})
|
| 417 |
)
|
| 418 |
-
.finally(() => (loading = false));
|
| 419 |
}
|
| 420 |
}
|
| 421 |
|
|
@@ -447,9 +447,9 @@
|
|
| 447 |
$effect(() => {
|
| 448 |
const streaming = isConversationStreaming(messages);
|
| 449 |
if (streaming) {
|
| 450 |
-
loading = true;
|
| 451 |
} else if (!pending) {
|
| 452 |
-
loading = false;
|
| 453 |
}
|
| 454 |
|
| 455 |
if (!streaming && browser) {
|
|
@@ -478,7 +478,7 @@
|
|
| 478 |
}
|
| 479 |
|
| 480 |
$isAborted = true;
|
| 481 |
-
loading = false;
|
| 482 |
});
|
| 483 |
|
| 484 |
let title = $derived.by(() => {
|
|
@@ -494,7 +494,7 @@
|
|
| 494 |
</svelte:head>
|
| 495 |
|
| 496 |
<ChatWindow
|
| 497 |
-
{loading}
|
| 498 |
{pending}
|
| 499 |
messages={messagesPath as Message[]}
|
| 500 |
{messagesAlternatives}
|
|
|
|
| 26 |
import "katex/dist/katex.min.css";
|
| 27 |
import { updateDebouncer } from "$lib/utils/updates.js";
|
| 28 |
import SubscribeModal from "$lib/components/SubscribeModal.svelte";
|
| 29 |
+
import { loading } from "$lib/stores/loading.js";
|
| 30 |
|
| 31 |
let { data = $bindable() } = $props();
|
| 32 |
|
|
|
|
| 33 |
let pending = $state(false);
|
| 34 |
let initialRun = true;
|
| 35 |
let showSubscribeModal = $state(false);
|
|
|
|
| 96 |
|
| 97 |
async function convFromShared() {
|
| 98 |
try {
|
| 99 |
+
$loading = true;
|
| 100 |
const res = await fetch(`${base}/conversation`, {
|
| 101 |
method: "POST",
|
| 102 |
headers: {
|
|
|
|
| 135 |
}): Promise<void> {
|
| 136 |
try {
|
| 137 |
$isAborted = false;
|
| 138 |
+
$loading = true;
|
| 139 |
pending = true;
|
| 140 |
const base64Files = await Promise.all(
|
| 141 |
(files ?? []).map((file) =>
|
|
|
|
| 335 |
}
|
| 336 |
console.error(err);
|
| 337 |
} finally {
|
| 338 |
+
$loading = false;
|
| 339 |
pending = false;
|
| 340 |
await invalidateAll();
|
| 341 |
}
|
|
|
|
| 348 |
if (r.ok) {
|
| 349 |
setTimeout(() => {
|
| 350 |
$isAborted = true;
|
| 351 |
+
$loading = false;
|
| 352 |
}, 500);
|
| 353 |
} else {
|
| 354 |
$isAborted = true;
|
| 355 |
+
$loading = false;
|
| 356 |
}
|
| 357 |
});
|
| 358 |
}
|
|
|
|
| 375 |
const streaming = isConversationStreaming(messages);
|
| 376 |
if (streaming) {
|
| 377 |
addBackgroundGeneration({ id: page.params.id, startedAt: Date.now() });
|
| 378 |
+
$loading = true;
|
| 379 |
}
|
| 380 |
});
|
| 381 |
|
|
|
|
| 388 |
await goto(`${base}/conversation/${convId}`, { invalidateAll: true });
|
| 389 |
})
|
| 390 |
.then(async () => await writeMessage({ prompt: content }))
|
| 391 |
+
.finally(() => ($loading = false));
|
| 392 |
}
|
| 393 |
}
|
| 394 |
|
|
|
|
| 415 |
isRetry: true,
|
| 416 |
})
|
| 417 |
)
|
| 418 |
+
.finally(() => ($loading = false));
|
| 419 |
}
|
| 420 |
}
|
| 421 |
|
|
|
|
| 447 |
$effect(() => {
|
| 448 |
const streaming = isConversationStreaming(messages);
|
| 449 |
if (streaming) {
|
| 450 |
+
$loading = true;
|
| 451 |
} else if (!pending) {
|
| 452 |
+
$loading = false;
|
| 453 |
}
|
| 454 |
|
| 455 |
if (!streaming && browser) {
|
|
|
|
| 478 |
}
|
| 479 |
|
| 480 |
$isAborted = true;
|
| 481 |
+
$loading = false;
|
| 482 |
});
|
| 483 |
|
| 484 |
let title = $derived.by(() => {
|
|
|
|
| 494 |
</svelte:head>
|
| 495 |
|
| 496 |
<ChatWindow
|
| 497 |
+
loading={$loading}
|
| 498 |
{pending}
|
| 499 |
messages={messagesPath as Message[]}
|
| 500 |
{messagesAlternatives}
|