Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
feat: lazy stream conversations load function (#1553)
Browse files* feat: lazy stream conversations load function
* fix: lint
* feat: add animation
* feat: skip db call if no convs
- src/lib/components/MobileNav.svelte +6 -2
- src/lib/components/NavConversationItem.svelte +2 -2
- src/lib/components/NavMenu.svelte +32 -16
- src/lib/types/ConvSidebar.ts +1 -1
- src/routes/+layout.server.ts +83 -50
- src/routes/+layout.svelte +10 -8
- src/routes/conversation/[id]/+page.svelte +12 -5
- src/routes/settings/(nav)/assistants/[assistantId]/+page.svelte +1 -1
- src/routes/settings/+layout.server.ts +1 -1
src/lib/components/MobileNav.svelte
CHANGED
|
@@ -10,7 +10,7 @@
|
|
| 10 |
import IconNew from "$lib/components/icons/IconNew.svelte";
|
| 11 |
|
| 12 |
export let isOpen = false;
|
| 13 |
-
export let title: string | undefined;
|
| 14 |
|
| 15 |
$: title = title ?? "New Chat";
|
| 16 |
|
|
@@ -40,7 +40,11 @@
|
|
| 40 |
aria-label="Open menu"
|
| 41 |
bind:this={openEl}><CarbonTextAlignJustify /></button
|
| 42 |
>
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
<a
|
| 45 |
class:invisible={!$page.params.id}
|
| 46 |
href="{base}/"
|
|
|
|
| 10 |
import IconNew from "$lib/components/icons/IconNew.svelte";
|
| 11 |
|
| 12 |
export let isOpen = false;
|
| 13 |
+
export let title: Promise<string | undefined> | string;
|
| 14 |
|
| 15 |
$: title = title ?? "New Chat";
|
| 16 |
|
|
|
|
| 40 |
aria-label="Open menu"
|
| 41 |
bind:this={openEl}><CarbonTextAlignJustify /></button
|
| 42 |
>
|
| 43 |
+
{#await title}
|
| 44 |
+
<div class="flex h-full items-center justify-center" />
|
| 45 |
+
{:then title}
|
| 46 |
+
<span class="truncate px-4">{title ?? ""}</span>
|
| 47 |
+
{/await}
|
| 48 |
<a
|
| 49 |
class:invisible={!$page.params.id}
|
| 50 |
href="{base}/"
|
src/lib/components/NavConversationItem.svelte
CHANGED
|
@@ -34,9 +34,9 @@
|
|
| 34 |
{#if confirmDelete}
|
| 35 |
<span class="mr-1 font-semibold"> Delete </span>
|
| 36 |
{/if}
|
| 37 |
-
{#if conv.
|
| 38 |
<img
|
| 39 |
-
src="{base}
|
| 40 |
alt="Assistant avatar"
|
| 41 |
class="mr-1.5 inline size-4 flex-none rounded-full object-cover"
|
| 42 |
/>
|
|
|
|
| 34 |
{#if confirmDelete}
|
| 35 |
<span class="mr-1 font-semibold"> Delete </span>
|
| 36 |
{/if}
|
| 37 |
+
{#if conv.avatarUrl}
|
| 38 |
<img
|
| 39 |
+
src="{base}{conv.avatarUrl}"
|
| 40 |
alt="Assistant avatar"
|
| 41 |
class="mr-1.5 inline size-4 flex-none rounded-full object-cover"
|
| 42 |
/>
|
src/lib/components/NavMenu.svelte
CHANGED
|
@@ -11,7 +11,8 @@
|
|
| 11 |
import type { Model } from "$lib/types/Model";
|
| 12 |
import { page } from "$app/stores";
|
| 13 |
|
| 14 |
-
|
|
|
|
| 15 |
export let canLogin: boolean;
|
| 16 |
export let user: LayoutData["user"];
|
| 17 |
|
|
@@ -25,16 +26,16 @@
|
|
| 25 |
new Date().setMonth(new Date().getMonth() - 1),
|
| 26 |
];
|
| 27 |
|
| 28 |
-
$: groupedConversations = {
|
| 29 |
-
today:
|
| 30 |
-
week:
|
| 31 |
({ updatedAt }) => updatedAt.getTime() > dateRanges[1] && updatedAt.getTime() < dateRanges[0]
|
| 32 |
),
|
| 33 |
-
month:
|
| 34 |
({ updatedAt }) => updatedAt.getTime() > dateRanges[2] && updatedAt.getTime() < dateRanges[1]
|
| 35 |
),
|
| 36 |
-
older:
|
| 37 |
-
};
|
| 38 |
|
| 39 |
const titles: { [key: string]: string } = {
|
| 40 |
today: "Today",
|
|
@@ -65,16 +66,31 @@
|
|
| 65 |
<div
|
| 66 |
class="scrollbar-custom flex flex-col gap-1 overflow-y-auto rounded-r-xl from-gray-50 px-3 pb-3 pt-2 text-[.9rem] dark:from-gray-800/30 max-sm:bg-gradient-to-t md:bg-gradient-to-l"
|
| 67 |
>
|
| 68 |
-
{#
|
| 69 |
-
{#if
|
| 70 |
-
<
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
| 76 |
{/if}
|
| 77 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
</div>
|
| 79 |
<div
|
| 80 |
class="mt-0.5 flex flex-col gap-1 rounded-r-xl p-3 text-sm md:bg-gradient-to-l md:from-gray-50 md:dark:from-gray-800/30"
|
|
|
|
| 11 |
import type { Model } from "$lib/types/Model";
|
| 12 |
import { page } from "$app/stores";
|
| 13 |
|
| 14 |
+
import { fade } from "svelte/transition";
|
| 15 |
+
export let conversations: Promise<ConvSidebar[]>;
|
| 16 |
export let canLogin: boolean;
|
| 17 |
export let user: LayoutData["user"];
|
| 18 |
|
|
|
|
| 26 |
new Date().setMonth(new Date().getMonth() - 1),
|
| 27 |
];
|
| 28 |
|
| 29 |
+
$: groupedConversations = conversations.then((convs) => ({
|
| 30 |
+
today: convs.filter(({ updatedAt }) => updatedAt.getTime() > dateRanges[0]),
|
| 31 |
+
week: convs.filter(
|
| 32 |
({ updatedAt }) => updatedAt.getTime() > dateRanges[1] && updatedAt.getTime() < dateRanges[0]
|
| 33 |
),
|
| 34 |
+
month: convs.filter(
|
| 35 |
({ updatedAt }) => updatedAt.getTime() > dateRanges[2] && updatedAt.getTime() < dateRanges[1]
|
| 36 |
),
|
| 37 |
+
older: convs.filter(({ updatedAt }) => updatedAt.getTime() < dateRanges[2]),
|
| 38 |
+
}));
|
| 39 |
|
| 40 |
const titles: { [key: string]: string } = {
|
| 41 |
today: "Today",
|
|
|
|
| 66 |
<div
|
| 67 |
class="scrollbar-custom flex flex-col gap-1 overflow-y-auto rounded-r-xl from-gray-50 px-3 pb-3 pt-2 text-[.9rem] dark:from-gray-800/30 max-sm:bg-gradient-to-t md:bg-gradient-to-l"
|
| 68 |
>
|
| 69 |
+
{#await groupedConversations}
|
| 70 |
+
{#if $page.data.nConversations > 0}
|
| 71 |
+
<div class="overflow-y-hidden">
|
| 72 |
+
<div class="flex animate-pulse flex-col gap-4">
|
| 73 |
+
<div class="h-4 w-24 rounded bg-gray-200 dark:bg-gray-700" />
|
| 74 |
+
{#each Array(100) as _}
|
| 75 |
+
<div class="ml-2 h-5 w-4/5 gap-5 rounded bg-gray-200 dark:bg-gray-700" />
|
| 76 |
+
{/each}
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
{/if}
|
| 80 |
+
{:then groupedConversations}
|
| 81 |
+
<div transition:fade class="flex flex-col gap-1">
|
| 82 |
+
{#each Object.entries(groupedConversations) as [group, convs]}
|
| 83 |
+
{#if convs.length}
|
| 84 |
+
<h4 class="mb-1.5 mt-4 pl-0.5 text-sm text-gray-400 first:mt-0 dark:text-gray-500">
|
| 85 |
+
{titles[group]}
|
| 86 |
+
</h4>
|
| 87 |
+
{#each convs as conv}
|
| 88 |
+
<NavConversationItem on:editConversationTitle on:deleteConversation {conv} />
|
| 89 |
+
{/each}
|
| 90 |
+
{/if}
|
| 91 |
+
{/each}
|
| 92 |
+
</div>
|
| 93 |
+
{/await}
|
| 94 |
</div>
|
| 95 |
<div
|
| 96 |
class="mt-0.5 flex flex-col gap-1 rounded-r-xl p-3 text-sm md:bg-gradient-to-l md:from-gray-50 md:dark:from-gray-800/30"
|
src/lib/types/ConvSidebar.ts
CHANGED
|
@@ -4,5 +4,5 @@ export interface ConvSidebar {
|
|
| 4 |
updatedAt: Date;
|
| 5 |
model?: string;
|
| 6 |
assistantId?: string;
|
| 7 |
-
|
| 8 |
}
|
|
|
|
| 4 |
updatedAt: Date;
|
| 5 |
model?: string;
|
| 6 |
assistantId?: string;
|
| 7 |
+
avatarUrl?: string;
|
| 8 |
}
|
src/routes/+layout.server.ts
CHANGED
|
@@ -51,38 +51,52 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => {
|
|
| 51 |
})
|
| 52 |
: null;
|
| 53 |
|
| 54 |
-
const
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
const userAssistants = settings?.assistants?.map((assistantId) => assistantId.toString()) ?? [];
|
| 71 |
const userAssistantsSet = new Set(userAssistants);
|
| 72 |
|
| 73 |
-
const
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
const messagesBeforeLogin = env.MESSAGES_BEFORE_LOGIN ? parseInt(env.MESSAGES_BEFORE_LOGIN) : 0;
|
| 81 |
|
| 82 |
let loginRequired = false;
|
| 83 |
|
| 84 |
if (requiresUser && !locals.user && messagesBeforeLogin) {
|
| 85 |
-
if (
|
| 86 |
loginRequired = true;
|
| 87 |
} else {
|
| 88 |
// get the number of messages where `from === "assistant"` across all conversations.
|
|
@@ -129,25 +143,42 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => {
|
|
| 129 |
);
|
| 130 |
|
| 131 |
return {
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
settings: {
|
| 152 |
searchEnabled: !!(
|
| 153 |
env.SERPAPI_KEY ||
|
|
@@ -223,15 +254,17 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => {
|
|
| 223 |
type: "community",
|
| 224 |
review: ReviewStatus.APPROVED,
|
| 225 |
}),
|
| 226 |
-
assistants: assistants
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
|
|
|
|
|
|
| 235 |
user: locals.user && {
|
| 236 |
id: locals.user._id.toString(),
|
| 237 |
username: locals.user.username,
|
|
|
|
| 51 |
})
|
| 52 |
: null;
|
| 53 |
|
| 54 |
+
const nConversations = await collections.conversations.countDocuments(authCondition(locals));
|
| 55 |
+
|
| 56 |
+
const conversations =
|
| 57 |
+
nConversations === 0
|
| 58 |
+
? Promise.resolve([])
|
| 59 |
+
: collections.conversations
|
| 60 |
+
.find(authCondition(locals))
|
| 61 |
+
.sort({ updatedAt: -1 })
|
| 62 |
+
.project<
|
| 63 |
+
Pick<
|
| 64 |
+
Conversation,
|
| 65 |
+
"title" | "model" | "_id" | "updatedAt" | "createdAt" | "assistantId"
|
| 66 |
+
>
|
| 67 |
+
>({
|
| 68 |
+
title: 1,
|
| 69 |
+
model: 1,
|
| 70 |
+
_id: 1,
|
| 71 |
+
updatedAt: 1,
|
| 72 |
+
createdAt: 1,
|
| 73 |
+
assistantId: 1,
|
| 74 |
+
})
|
| 75 |
+
.limit(300)
|
| 76 |
+
.toArray();
|
| 77 |
|
| 78 |
const userAssistants = settings?.assistants?.map((assistantId) => assistantId.toString()) ?? [];
|
| 79 |
const userAssistantsSet = new Set(userAssistants);
|
| 80 |
|
| 81 |
+
const assistants = conversations.then((conversations) =>
|
| 82 |
+
collections.assistants
|
| 83 |
+
.find({
|
| 84 |
+
_id: {
|
| 85 |
+
$in: [
|
| 86 |
+
...userAssistants.map((el) => new ObjectId(el)),
|
| 87 |
+
...(conversations.map((conv) => conv.assistantId).filter((el) => !!el) as ObjectId[]),
|
| 88 |
+
],
|
| 89 |
+
},
|
| 90 |
+
})
|
| 91 |
+
.toArray()
|
| 92 |
+
);
|
| 93 |
|
| 94 |
const messagesBeforeLogin = env.MESSAGES_BEFORE_LOGIN ? parseInt(env.MESSAGES_BEFORE_LOGIN) : 0;
|
| 95 |
|
| 96 |
let loginRequired = false;
|
| 97 |
|
| 98 |
if (requiresUser && !locals.user && messagesBeforeLogin) {
|
| 99 |
+
if (nConversations > messagesBeforeLogin) {
|
| 100 |
loginRequired = true;
|
| 101 |
} else {
|
| 102 |
// get the number of messages where `from === "assistant"` across all conversations.
|
|
|
|
| 143 |
);
|
| 144 |
|
| 145 |
return {
|
| 146 |
+
nConversations,
|
| 147 |
+
conversations: conversations.then(
|
| 148 |
+
async (convs) =>
|
| 149 |
+
await Promise.all(
|
| 150 |
+
convs.map(async (conv) => {
|
| 151 |
+
if (settings?.hideEmojiOnSidebar) {
|
| 152 |
+
conv.title = conv.title.replace(/\p{Emoji}/gu, "");
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
// remove invalid unicode and trim whitespaces
|
| 156 |
+
conv.title = conv.title.replace(/\uFFFD/gu, "").trimStart();
|
| 157 |
+
|
| 158 |
+
let avatarUrl: string | undefined = undefined;
|
| 159 |
+
|
| 160 |
+
if (conv.assistantId) {
|
| 161 |
+
const hash = (
|
| 162 |
+
await collections.assistants.findOne({
|
| 163 |
+
_id: new ObjectId(conv.assistantId),
|
| 164 |
+
})
|
| 165 |
+
)?.avatar;
|
| 166 |
+
if (hash) {
|
| 167 |
+
avatarUrl = `/settings/assistants/${conv.assistantId}/avatar.jpg?hash=${hash}`;
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
return {
|
| 172 |
+
id: conv._id.toString(),
|
| 173 |
+
title: conv.title,
|
| 174 |
+
model: conv.model ?? defaultModel,
|
| 175 |
+
updatedAt: conv.updatedAt,
|
| 176 |
+
assistantId: conv.assistantId?.toString(),
|
| 177 |
+
avatarUrl,
|
| 178 |
+
} satisfies ConvSidebar;
|
| 179 |
+
})
|
| 180 |
+
)
|
| 181 |
+
),
|
| 182 |
settings: {
|
| 183 |
searchEnabled: !!(
|
| 184 |
env.SERPAPI_KEY ||
|
|
|
|
| 254 |
type: "community",
|
| 255 |
review: ReviewStatus.APPROVED,
|
| 256 |
}),
|
| 257 |
+
assistants: assistants.then((assistants) =>
|
| 258 |
+
assistants
|
| 259 |
+
.filter((el) => userAssistantsSet.has(el._id.toString()))
|
| 260 |
+
.map((el) => ({
|
| 261 |
+
...el,
|
| 262 |
+
_id: el._id.toString(),
|
| 263 |
+
createdById: undefined,
|
| 264 |
+
createdByMe:
|
| 265 |
+
el.createdById.toString() === (locals.user?._id ?? locals.sessionId).toString(),
|
| 266 |
+
}))
|
| 267 |
+
),
|
| 268 |
user: locals.user && {
|
| 269 |
id: locals.user._id.toString(),
|
| 270 |
username: locals.user.username,
|
src/routes/+layout.svelte
CHANGED
|
@@ -100,15 +100,17 @@
|
|
| 100 |
$: if ($error) onError();
|
| 101 |
|
| 102 |
$: if ($titleUpdate) {
|
| 103 |
-
|
|
|
|
| 104 |
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
|
| 111 |
-
|
|
|
|
| 112 |
}
|
| 113 |
|
| 114 |
const settings = createSettingsStore(data.settings);
|
|
@@ -147,7 +149,7 @@
|
|
| 147 |
|
| 148 |
$: mobileNavTitle = ["/models", "/assistants", "/privacy"].includes($page.route.id ?? "")
|
| 149 |
? ""
|
| 150 |
-
: data.conversations.find((conv) => conv.id === $page.params.id)?.title;
|
| 151 |
</script>
|
| 152 |
|
| 153 |
<svelte:head>
|
|
|
|
| 100 |
$: if ($error) onError();
|
| 101 |
|
| 102 |
$: if ($titleUpdate) {
|
| 103 |
+
data.conversations.then((convs) => {
|
| 104 |
+
const convIdx = convs.findIndex(({ id }) => id === $titleUpdate?.convId);
|
| 105 |
|
| 106 |
+
if (convIdx != -1) {
|
| 107 |
+
convs[convIdx].title = $titleUpdate?.title ?? convs[convIdx].title;
|
| 108 |
+
}
|
| 109 |
+
// update data.conversations
|
| 110 |
+
data.conversations = Promise.resolve([...convs]);
|
| 111 |
|
| 112 |
+
$titleUpdate = null;
|
| 113 |
+
});
|
| 114 |
}
|
| 115 |
|
| 116 |
const settings = createSettingsStore(data.settings);
|
|
|
|
| 149 |
|
| 150 |
$: mobileNavTitle = ["/models", "/assistants", "/privacy"].includes($page.route.id ?? "")
|
| 151 |
? ""
|
| 152 |
+
: data.conversations.then((convs) => convs.find((conv) => conv.id === $page.params.id)?.title);
|
| 153 |
</script>
|
| 154 |
|
| 155 |
<svelte:head>
|
src/routes/conversation/[id]/+page.svelte
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
import { isAborted } from "$lib/stores/isAborted";
|
| 5 |
import { onMount } from "svelte";
|
| 6 |
import { page } from "$app/stores";
|
| 7 |
-
import { goto,
|
| 8 |
import { base } from "$app/paths";
|
| 9 |
import { shareConversation } from "$lib/shareConversation";
|
| 10 |
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
|
@@ -24,6 +24,7 @@
|
|
| 24 |
import { createConvTreeStore } from "$lib/stores/convTree";
|
| 25 |
import type { v4 } from "uuid";
|
| 26 |
import { useSettingsStore } from "$lib/stores/settings.js";
|
|
|
|
| 27 |
|
| 28 |
export let data;
|
| 29 |
|
|
@@ -247,7 +248,9 @@
|
|
| 247 |
) {
|
| 248 |
$error = update.message ?? "An error has occurred";
|
| 249 |
} else if (update.type === MessageUpdateType.Title) {
|
| 250 |
-
const convInData = data.conversations.
|
|
|
|
|
|
|
| 251 |
if (convInData) {
|
| 252 |
convInData.title = update.title;
|
| 253 |
|
|
@@ -280,7 +283,7 @@
|
|
| 280 |
} finally {
|
| 281 |
loading = false;
|
| 282 |
pending = false;
|
| 283 |
-
await
|
| 284 |
}
|
| 285 |
}
|
| 286 |
|
|
@@ -376,14 +379,18 @@
|
|
| 376 |
}
|
| 377 |
|
| 378 |
$: $page.params.id, (($isAborted = true), (loading = false), ($convTreeStore.editing = null));
|
| 379 |
-
$: title = data.conversations.
|
|
|
|
|
|
|
| 380 |
|
| 381 |
const convTreeStore = createConvTreeStore();
|
| 382 |
const settings = useSettingsStore();
|
| 383 |
</script>
|
| 384 |
|
| 385 |
<svelte:head>
|
| 386 |
-
|
|
|
|
|
|
|
| 387 |
<link
|
| 388 |
rel="stylesheet"
|
| 389 |
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css"
|
|
|
|
| 4 |
import { isAborted } from "$lib/stores/isAborted";
|
| 5 |
import { onMount } from "svelte";
|
| 6 |
import { page } from "$app/stores";
|
| 7 |
+
import { goto, invalidate } from "$app/navigation";
|
| 8 |
import { base } from "$app/paths";
|
| 9 |
import { shareConversation } from "$lib/shareConversation";
|
| 10 |
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
|
|
|
| 24 |
import { createConvTreeStore } from "$lib/stores/convTree";
|
| 25 |
import type { v4 } from "uuid";
|
| 26 |
import { useSettingsStore } from "$lib/stores/settings.js";
|
| 27 |
+
import { UrlDependency } from "$lib/types/UrlDependency.js";
|
| 28 |
|
| 29 |
export let data;
|
| 30 |
|
|
|
|
| 248 |
) {
|
| 249 |
$error = update.message ?? "An error has occurred";
|
| 250 |
} else if (update.type === MessageUpdateType.Title) {
|
| 251 |
+
const convInData = await data.conversations.then((convs) =>
|
| 252 |
+
convs.find(({ id }) => id === $page.params.id)
|
| 253 |
+
);
|
| 254 |
if (convInData) {
|
| 255 |
convInData.title = update.title;
|
| 256 |
|
|
|
|
| 283 |
} finally {
|
| 284 |
loading = false;
|
| 285 |
pending = false;
|
| 286 |
+
await invalidate(UrlDependency.Conversation);
|
| 287 |
}
|
| 288 |
}
|
| 289 |
|
|
|
|
| 379 |
}
|
| 380 |
|
| 381 |
$: $page.params.id, (($isAborted = true), (loading = false), ($convTreeStore.editing = null));
|
| 382 |
+
$: title = data.conversations.then(
|
| 383 |
+
(convs) => convs.find((conv) => conv.id === $page.params.id)?.title ?? data.title
|
| 384 |
+
);
|
| 385 |
|
| 386 |
const convTreeStore = createConvTreeStore();
|
| 387 |
const settings = useSettingsStore();
|
| 388 |
</script>
|
| 389 |
|
| 390 |
<svelte:head>
|
| 391 |
+
{#await title then title}
|
| 392 |
+
<title>{title}</title>
|
| 393 |
+
{/await}
|
| 394 |
<link
|
| 395 |
rel="stylesheet"
|
| 396 |
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css"
|
src/routes/settings/(nav)/assistants/[assistantId]/+page.svelte
CHANGED
|
@@ -103,7 +103,7 @@
|
|
| 103 |
>
|
| 104 |
<div class="w-full sm:w-auto">
|
| 105 |
<button
|
| 106 |
-
class="mx-auto my-2 flex w-
|
| 107 |
name="Activate model"
|
| 108 |
on:click|stopPropagation={() => {
|
| 109 |
settings.instantSet({
|
|
|
|
| 103 |
>
|
| 104 |
<div class="w-full sm:w-auto">
|
| 105 |
<button
|
| 106 |
+
class="mx-auto my-2 flex w-min items-center justify-center rounded-full bg-black px-3 py-1 text-base !text-white"
|
| 107 |
name="Activate model"
|
| 108 |
on:click|stopPropagation={() => {
|
| 109 |
settings.instantSet({
|
src/routes/settings/+layout.server.ts
CHANGED
|
@@ -18,7 +18,7 @@ export const load = (async ({ locals, parent }) => {
|
|
| 18 |
}
|
| 19 |
|
| 20 |
return {
|
| 21 |
-
assistants: assistants.map((el) => ({
|
| 22 |
...el,
|
| 23 |
reported: reportsByUser.includes(el._id),
|
| 24 |
})),
|
|
|
|
| 18 |
}
|
| 19 |
|
| 20 |
return {
|
| 21 |
+
assistants: (await assistants).map((el) => ({
|
| 22 |
...el,
|
| 23 |
reported: reportsByUser.includes(el._id),
|
| 24 |
})),
|