Make assistant settings form fill the modal (#900)
Browse files* Make assistant settings form fill the modal
* tweaks
* Use layout groups instead of parsing the url
* fix console error & overflows
* scroll on dialog instead
---------
Co-authored-by: Victor Mustar <[email protected]>
- src/lib/components/AssistantSettings.svelte +19 -18
- src/routes/settings/(nav)/+layout.svelte +135 -0
- src/routes/settings/{+page.svelte β (nav)/+page.svelte} +0 -0
- src/routes/settings/{+server.ts β (nav)/+server.ts} +0 -0
- src/routes/settings/{[...model] β (nav)/[...model]}/+page.svelte +0 -0
- src/routes/settings/{[...model] β (nav)/[...model]}/+page.ts +1 -1
- src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/+page.server.ts +0 -0
- src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/+page.svelte +0 -0
- src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/+page.ts +0 -0
- src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/ReportModal.svelte +0 -0
- src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/avatar.jpg/+server.ts +0 -0
- src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/edit/+page.server.ts +0 -0
- src/routes/settings/{assistants/[assistantId]/edit/+page.svelte β (nav)/assistants/[assistantId]/edit/[email protected]} +0 -0
- src/routes/settings/{assistants β (nav)/assistants}/new/+page.server.ts +0 -0
- src/routes/settings/{assistants/new/+page.svelte β (nav)/assistants/new/[email protected]} +0 -0
- src/routes/settings/+layout.svelte +2 -112
src/lib/components/AssistantSettings.svelte
CHANGED
|
@@ -8,8 +8,8 @@
|
|
| 8 |
import { base } from "$app/paths";
|
| 9 |
import CarbonPen from "~icons/carbon/pen";
|
| 10 |
import CarbonUpload from "~icons/carbon/upload";
|
|
|
|
| 11 |
import { useSettingsStore } from "$lib/stores/settings";
|
| 12 |
-
import IconLoading from "./icons/IconLoading.svelte";
|
| 13 |
|
| 14 |
type ActionData = {
|
| 15 |
error: boolean;
|
|
@@ -75,7 +75,7 @@
|
|
| 75 |
|
| 76 |
<form
|
| 77 |
method="POST"
|
| 78 |
-
class="flex h-full flex-col"
|
| 79 |
enctype="multipart/form-data"
|
| 80 |
use:enhance={async ({ formData }) => {
|
| 81 |
loading = true;
|
|
@@ -110,7 +110,9 @@
|
|
| 110 |
}}
|
| 111 |
>
|
| 112 |
{#if assistant}
|
| 113 |
-
<h2 class="text-xl font-semibold">
|
|
|
|
|
|
|
| 114 |
<p class="mb-6 text-sm text-gray-500">
|
| 115 |
Modifying an existing assistant will propagate those changes to all users.
|
| 116 |
</p>
|
|
@@ -123,10 +125,10 @@
|
|
| 123 |
</p>
|
| 124 |
{/if}
|
| 125 |
|
| 126 |
-
<div class="
|
| 127 |
<div class="flex flex-col gap-4">
|
| 128 |
<div>
|
| 129 |
-
<
|
| 130 |
<input
|
| 131 |
type="file"
|
| 132 |
accept="image/*"
|
|
@@ -185,7 +187,7 @@
|
|
| 185 |
</div>
|
| 186 |
|
| 187 |
<label>
|
| 188 |
-
<
|
| 189 |
<input
|
| 190 |
name="name"
|
| 191 |
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
|
|
@@ -196,10 +198,10 @@
|
|
| 196 |
</label>
|
| 197 |
|
| 198 |
<label>
|
| 199 |
-
<
|
| 200 |
<textarea
|
| 201 |
name="description"
|
| 202 |
-
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
|
| 203 |
placeholder="He knows everything about python"
|
| 204 |
value={assistant?.description ?? ""}
|
| 205 |
/>
|
|
@@ -207,7 +209,7 @@
|
|
| 207 |
</label>
|
| 208 |
|
| 209 |
<label>
|
| 210 |
-
<
|
| 211 |
<select name="modelId" class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2">
|
| 212 |
{#each models.filter((model) => !model.unlisted) as model}
|
| 213 |
<option
|
|
@@ -222,8 +224,8 @@
|
|
| 222 |
</label>
|
| 223 |
|
| 224 |
<label>
|
| 225 |
-
<
|
| 226 |
-
<div class="flex flex-col gap-2
|
| 227 |
<input
|
| 228 |
name="exampleInput1"
|
| 229 |
bind:value={inputMessage1}
|
|
@@ -256,7 +258,7 @@
|
|
| 256 |
</div>
|
| 257 |
|
| 258 |
<label class="flex flex-col">
|
| 259 |
-
<
|
| 260 |
<textarea
|
| 261 |
name="preprompt"
|
| 262 |
class="min-h-[8lh] flex-1 rounded-lg border-2 border-gray-200 bg-gray-100 p-2 text-sm"
|
|
@@ -267,24 +269,23 @@
|
|
| 267 |
</label>
|
| 268 |
</div>
|
| 269 |
|
| 270 |
-
<div class="mt-
|
| 271 |
<a
|
| 272 |
href={assistant ? `${base}/settings/assistants/${assistant?._id}` : `${base}/settings`}
|
| 273 |
-
class="rounded-full bg-gray-200 px-
|
| 274 |
>
|
|
|
|
|
|
|
| 275 |
<button
|
| 276 |
type="submit"
|
| 277 |
disabled={loading}
|
| 278 |
aria-disabled={loading}
|
| 279 |
-
class="rounded-full bg-black px-8 py-2 font-semibold
|
| 280 |
class:bg-gray-200={loading}
|
| 281 |
class:text-gray-600={loading}
|
| 282 |
class:text-white={!loading}
|
| 283 |
>
|
| 284 |
{assistant ? "Save" : "Create"}
|
| 285 |
-
{#if loading}
|
| 286 |
-
<IconLoading classNames="ml-2 h-min" />
|
| 287 |
-
{/if}
|
| 288 |
</button>
|
| 289 |
</div>
|
| 290 |
</form>
|
|
|
|
| 8 |
import { base } from "$app/paths";
|
| 9 |
import CarbonPen from "~icons/carbon/pen";
|
| 10 |
import CarbonUpload from "~icons/carbon/upload";
|
| 11 |
+
|
| 12 |
import { useSettingsStore } from "$lib/stores/settings";
|
|
|
|
| 13 |
|
| 14 |
type ActionData = {
|
| 15 |
error: boolean;
|
|
|
|
| 75 |
|
| 76 |
<form
|
| 77 |
method="POST"
|
| 78 |
+
class="flex h-full flex-col overflow-y-auto p-4 md:p-8"
|
| 79 |
enctype="multipart/form-data"
|
| 80 |
use:enhance={async ({ formData }) => {
|
| 81 |
loading = true;
|
|
|
|
| 110 |
}}
|
| 111 |
>
|
| 112 |
{#if assistant}
|
| 113 |
+
<h2 class="text-xl font-semibold">
|
| 114 |
+
Edit {assistant?.name ?? "assistant"}
|
| 115 |
+
</h2>
|
| 116 |
<p class="mb-6 text-sm text-gray-500">
|
| 117 |
Modifying an existing assistant will propagate those changes to all users.
|
| 118 |
</p>
|
|
|
|
| 125 |
</p>
|
| 126 |
{/if}
|
| 127 |
|
| 128 |
+
<div class="grid h-full w-full flex-1 grid-cols-2 gap-6 text-sm max-sm:grid-cols-1">
|
| 129 |
<div class="flex flex-col gap-4">
|
| 130 |
<div>
|
| 131 |
+
<div class="mb-1 block pb-2 text-sm font-semibold">Avatar</div>
|
| 132 |
<input
|
| 133 |
type="file"
|
| 134 |
accept="image/*"
|
|
|
|
| 187 |
</div>
|
| 188 |
|
| 189 |
<label>
|
| 190 |
+
<div class="mb-1 font-semibold">Name</div>
|
| 191 |
<input
|
| 192 |
name="name"
|
| 193 |
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
|
|
|
|
| 198 |
</label>
|
| 199 |
|
| 200 |
<label>
|
| 201 |
+
<div class="mb-1 font-semibold">Description</div>
|
| 202 |
<textarea
|
| 203 |
name="description"
|
| 204 |
+
class="h-15 w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
|
| 205 |
placeholder="He knows everything about python"
|
| 206 |
value={assistant?.description ?? ""}
|
| 207 |
/>
|
|
|
|
| 209 |
</label>
|
| 210 |
|
| 211 |
<label>
|
| 212 |
+
<div class="mb-1 font-semibold">Model</div>
|
| 213 |
<select name="modelId" class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2">
|
| 214 |
{#each models.filter((model) => !model.unlisted) as model}
|
| 215 |
<option
|
|
|
|
| 224 |
</label>
|
| 225 |
|
| 226 |
<label>
|
| 227 |
+
<div class="mb-1 font-semibold">User start messages</div>
|
| 228 |
+
<div class="flex flex-col gap-2">
|
| 229 |
<input
|
| 230 |
name="exampleInput1"
|
| 231 |
bind:value={inputMessage1}
|
|
|
|
| 258 |
</div>
|
| 259 |
|
| 260 |
<label class="flex flex-col">
|
| 261 |
+
<div class="mb-1 text-sm font-semibold">Instructions (system prompt)</div>
|
| 262 |
<textarea
|
| 263 |
name="preprompt"
|
| 264 |
class="min-h-[8lh] flex-1 rounded-lg border-2 border-gray-200 bg-gray-100 p-2 text-sm"
|
|
|
|
| 269 |
</label>
|
| 270 |
</div>
|
| 271 |
|
| 272 |
+
<div class="mt-6 flex justify-end gap-2">
|
| 273 |
<a
|
| 274 |
href={assistant ? `${base}/settings/assistants/${assistant?._id}` : `${base}/settings`}
|
| 275 |
+
class="flex items-center justify-center rounded-full bg-gray-200 px-5 py-2 font-semibold text-gray-600"
|
| 276 |
>
|
| 277 |
+
Cancel
|
| 278 |
+
</a>
|
| 279 |
<button
|
| 280 |
type="submit"
|
| 281 |
disabled={loading}
|
| 282 |
aria-disabled={loading}
|
| 283 |
+
class="flex items-center justify-center rounded-full bg-black px-8 py-2 font-semibold"
|
| 284 |
class:bg-gray-200={loading}
|
| 285 |
class:text-gray-600={loading}
|
| 286 |
class:text-white={!loading}
|
| 287 |
>
|
| 288 |
{assistant ? "Save" : "Create"}
|
|
|
|
|
|
|
|
|
|
| 289 |
</button>
|
| 290 |
</div>
|
| 291 |
</form>
|
src/routes/settings/(nav)/+layout.svelte
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { onMount } from "svelte";
|
| 3 |
+
import { base } from "$app/paths";
|
| 4 |
+
import { afterNavigate, goto } from "$app/navigation";
|
| 5 |
+
import { page } from "$app/stores";
|
| 6 |
+
import { useSettingsStore } from "$lib/stores/settings";
|
| 7 |
+
import CarbonClose from "~icons/carbon/close";
|
| 8 |
+
import CarbonArrowUpRight from "~icons/carbon/ArrowUpRight";
|
| 9 |
+
import CarbonAdd from "~icons/carbon/add";
|
| 10 |
+
|
| 11 |
+
import UserIcon from "~icons/carbon/user";
|
| 12 |
+
import type { LayoutData } from "../$types";
|
| 13 |
+
|
| 14 |
+
export let data: LayoutData;
|
| 15 |
+
|
| 16 |
+
let previousPage: string = base;
|
| 17 |
+
let assistantsSection: HTMLHeadingElement;
|
| 18 |
+
|
| 19 |
+
onMount(() => {
|
| 20 |
+
if ($page.params?.assistantId) {
|
| 21 |
+
assistantsSection.scrollIntoView();
|
| 22 |
+
}
|
| 23 |
+
});
|
| 24 |
+
|
| 25 |
+
afterNavigate(({ from }) => {
|
| 26 |
+
if (!from?.url.pathname.includes("settings")) {
|
| 27 |
+
previousPage = from?.url.toString() || previousPage;
|
| 28 |
+
}
|
| 29 |
+
});
|
| 30 |
+
|
| 31 |
+
const settings = useSettingsStore();
|
| 32 |
+
</script>
|
| 33 |
+
|
| 34 |
+
<div
|
| 35 |
+
class="grid h-full w-full grid-cols-1 grid-rows-[auto,1fr] content-start gap-x-8 overflow-hidden p-4 md:grid-cols-3 md:grid-rows-[auto,1fr] md:p-8"
|
| 36 |
+
>
|
| 37 |
+
<div class="col-span-1 mb-4 flex items-center justify-between md:col-span-3">
|
| 38 |
+
<h2 class="text-xl font-bold">Settings</h2>
|
| 39 |
+
<button
|
| 40 |
+
class="btn rounded-lg"
|
| 41 |
+
on:click={() => {
|
| 42 |
+
goto(previousPage);
|
| 43 |
+
}}
|
| 44 |
+
>
|
| 45 |
+
<CarbonClose class="text-xl text-gray-900 hover:text-black" />
|
| 46 |
+
</button>
|
| 47 |
+
</div>
|
| 48 |
+
<div
|
| 49 |
+
class="col-span-1 flex flex-col overflow-y-auto whitespace-nowrap max-md:-mx-4 max-md:h-[245px] max-md:border max-md:border-b-2 md:pr-6"
|
| 50 |
+
>
|
| 51 |
+
<h3 class="pb-3 pl-3 pt-2 text-[.8rem] text-gray-800 sm:pl-1">Models</h3>
|
| 52 |
+
|
| 53 |
+
{#each data.models.filter((el) => !el.unlisted) as model}
|
| 54 |
+
<a
|
| 55 |
+
href="{base}/settings/{model.id}"
|
| 56 |
+
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl
|
| 57 |
+
{model.id === $page.params.model ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 58 |
+
>
|
| 59 |
+
<div class="truncate">{model.displayName}</div>
|
| 60 |
+
{#if model.id === $settings.activeModel}
|
| 61 |
+
<div
|
| 62 |
+
class="ml-auto rounded-lg bg-black px-2 py-1.5 text-xs font-semibold leading-none text-white"
|
| 63 |
+
>
|
| 64 |
+
Active
|
| 65 |
+
</div>
|
| 66 |
+
{/if}
|
| 67 |
+
</a>
|
| 68 |
+
{/each}
|
| 69 |
+
<!-- if its huggingchat, the number of assistants owned by the user must be non-zero to show the UI -->
|
| 70 |
+
{#if data.enableAssistants}
|
| 71 |
+
<h3 bind:this={assistantsSection} class="pb-3 pl-3 pt-5 text-[.8rem] text-gray-800 sm:pl-1">
|
| 72 |
+
Assistants
|
| 73 |
+
</h3>
|
| 74 |
+
{#if !data.loginEnabled || (data.loginEnabled && !!data.user)}
|
| 75 |
+
<a
|
| 76 |
+
href="{base}/settings/assistants/new"
|
| 77 |
+
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl
|
| 78 |
+
{$page.url.pathname === `${base}/settings/assistants/new` ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 79 |
+
>
|
| 80 |
+
<CarbonAdd />
|
| 81 |
+
<div class="truncate">Create new assistant</div>
|
| 82 |
+
</a>
|
| 83 |
+
{/if}
|
| 84 |
+
{#each data.assistants as assistant}
|
| 85 |
+
<a
|
| 86 |
+
href="{base}/settings/assistants/{assistant._id.toString()}"
|
| 87 |
+
class="group flex h-10 flex-none items-center gap-2 pl-2 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl
|
| 88 |
+
{assistant._id.toString() === $page.params.assistantId ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 89 |
+
>
|
| 90 |
+
{#if assistant.avatar}
|
| 91 |
+
<img
|
| 92 |
+
src="{base}/settings/assistants/{assistant._id.toString()}/avatar.jpg?hash={assistant.avatar}"
|
| 93 |
+
alt="Avatar"
|
| 94 |
+
class="h-6 w-6 rounded-full object-cover"
|
| 95 |
+
/>
|
| 96 |
+
{:else}
|
| 97 |
+
<div
|
| 98 |
+
class="flex size-6 items-center justify-center rounded-full bg-gray-300 font-bold uppercase text-gray-500"
|
| 99 |
+
>
|
| 100 |
+
{assistant.name[0]}
|
| 101 |
+
</div>
|
| 102 |
+
{/if}
|
| 103 |
+
<div class="truncate">{assistant.name}</div>
|
| 104 |
+
{#if assistant._id.toString() === $settings.activeModel}
|
| 105 |
+
<div
|
| 106 |
+
class="ml-auto rounded-lg bg-black px-2 py-1.5 text-xs font-semibold leading-none text-white"
|
| 107 |
+
>
|
| 108 |
+
Active
|
| 109 |
+
</div>
|
| 110 |
+
{/if}
|
| 111 |
+
</a>
|
| 112 |
+
{/each}
|
| 113 |
+
<a
|
| 114 |
+
href="{base}/assistants"
|
| 115 |
+
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl"
|
| 116 |
+
><CarbonArrowUpRight class="mr-1.5 shrink-0 text-xs " />
|
| 117 |
+
<div class="truncate">Browse Assistants</div>
|
| 118 |
+
</a>
|
| 119 |
+
{/if}
|
| 120 |
+
|
| 121 |
+
<a
|
| 122 |
+
href="{base}/settings"
|
| 123 |
+
class="group mt-auto 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
|
| 124 |
+
{$page.url.pathname === `${base}/settings` ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 125 |
+
>
|
| 126 |
+
<UserIcon class="text-sm" />
|
| 127 |
+
Application Settings
|
| 128 |
+
</a>
|
| 129 |
+
</div>
|
| 130 |
+
<div
|
| 131 |
+
class="col-span-1 w-full overflow-y-auto overflow-x-clip px-1 max-md:pt-4 md:col-span-2 md:row-span-2"
|
| 132 |
+
>
|
| 133 |
+
<slot />
|
| 134 |
+
</div>
|
| 135 |
+
</div>
|
src/routes/settings/{+page.svelte β (nav)/+page.svelte}
RENAMED
|
File without changes
|
src/routes/settings/{+server.ts β (nav)/+server.ts}
RENAMED
|
File without changes
|
src/routes/settings/{[...model] β (nav)/[...model]}/+page.svelte
RENAMED
|
File without changes
|
src/routes/settings/{[...model] β (nav)/[...model]}/+page.ts
RENAMED
|
@@ -4,7 +4,7 @@ import { redirect } from "@sveltejs/kit";
|
|
| 4 |
export async function load({ parent, params }) {
|
| 5 |
const data = await parent();
|
| 6 |
|
| 7 |
-
const model = data.models.find(({ id }) => id === params.model);
|
| 8 |
|
| 9 |
if (!model || model.unlisted) {
|
| 10 |
throw redirect(302, `${base}/settings`);
|
|
|
|
| 4 |
export async function load({ parent, params }) {
|
| 5 |
const data = await parent();
|
| 6 |
|
| 7 |
+
const model = data.models.find((m: { id: string }) => m.id === params.model);
|
| 8 |
|
| 9 |
if (!model || model.unlisted) {
|
| 10 |
throw redirect(302, `${base}/settings`);
|
src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/+page.server.ts
RENAMED
|
File without changes
|
src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/+page.svelte
RENAMED
|
File without changes
|
src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/+page.ts
RENAMED
|
File without changes
|
src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/ReportModal.svelte
RENAMED
|
File without changes
|
src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/avatar.jpg/+server.ts
RENAMED
|
File without changes
|
src/routes/settings/{assistants β (nav)/assistants}/[assistantId]/edit/+page.server.ts
RENAMED
|
File without changes
|
src/routes/settings/{assistants/[assistantId]/edit/+page.svelte β (nav)/assistants/[assistantId]/edit/[email protected]}
RENAMED
|
File without changes
|
src/routes/settings/{assistants β (nav)/assistants}/new/+page.server.ts
RENAMED
|
File without changes
|
src/routes/settings/{assistants/new/+page.svelte β (nav)/assistants/new/[email protected]}
RENAMED
|
File without changes
|
src/routes/settings/+layout.svelte
CHANGED
|
@@ -1,27 +1,13 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
-
import { onMount } from "svelte";
|
| 3 |
import { base } from "$app/paths";
|
| 4 |
import { clickOutside } from "$lib/actions/clickOutside";
|
| 5 |
import { afterNavigate, goto } from "$app/navigation";
|
| 6 |
-
import { page } from "$app/stores";
|
| 7 |
import { useSettingsStore } from "$lib/stores/settings";
|
| 8 |
-
import CarbonClose from "~icons/carbon/close";
|
| 9 |
-
import CarbonArrowUpRight from "~icons/carbon/ArrowUpRight";
|
| 10 |
import CarbonCheckmark from "~icons/carbon/checkmark";
|
| 11 |
-
import CarbonAdd from "~icons/carbon/add";
|
| 12 |
|
| 13 |
-
import UserIcon from "~icons/carbon/user";
|
| 14 |
import { fade, fly } from "svelte/transition";
|
| 15 |
-
export let data;
|
| 16 |
|
| 17 |
let previousPage: string = base;
|
| 18 |
-
let assistantsSection: HTMLHeadingElement;
|
| 19 |
-
|
| 20 |
-
onMount(() => {
|
| 21 |
-
if ($page.params?.assistantId) {
|
| 22 |
-
assistantsSection.scrollIntoView();
|
| 23 |
-
}
|
| 24 |
-
});
|
| 25 |
|
| 26 |
afterNavigate(({ from }) => {
|
| 27 |
if (!from?.url.pathname.includes("settings")) {
|
|
@@ -42,105 +28,9 @@
|
|
| 42 |
use:clickOutside={() => {
|
| 43 |
goto(previousPage);
|
| 44 |
}}
|
| 45 |
-
class="
|
| 46 |
>
|
| 47 |
-
<
|
| 48 |
-
<h2 class="text-xl font-bold">Settings</h2>
|
| 49 |
-
<button
|
| 50 |
-
class="btn rounded-lg"
|
| 51 |
-
on:click={() => {
|
| 52 |
-
goto(previousPage);
|
| 53 |
-
}}
|
| 54 |
-
>
|
| 55 |
-
<CarbonClose class="text-xl text-gray-900 hover:text-black" />
|
| 56 |
-
</button>
|
| 57 |
-
</div>
|
| 58 |
-
<div
|
| 59 |
-
class="col-span-1 flex flex-col overflow-y-auto whitespace-nowrap max-md:-mx-4 max-md:h-[245px] max-md:border max-md:border-b-2 md:pr-6"
|
| 60 |
-
>
|
| 61 |
-
<h3 class="pb-3 pl-3 pt-2 text-[.8rem] text-gray-800 sm:pl-1">Models</h3>
|
| 62 |
-
|
| 63 |
-
{#each data.models.filter((el) => !el.unlisted) as model}
|
| 64 |
-
<a
|
| 65 |
-
href="{base}/settings/{model.id}"
|
| 66 |
-
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl
|
| 67 |
-
{model.id === $page.params.model ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 68 |
-
>
|
| 69 |
-
<div class="truncate">{model.displayName}</div>
|
| 70 |
-
{#if model.id === $settings.activeModel}
|
| 71 |
-
<div
|
| 72 |
-
class="ml-auto rounded-lg bg-black px-2 py-1.5 text-xs font-semibold leading-none text-white"
|
| 73 |
-
>
|
| 74 |
-
Active
|
| 75 |
-
</div>
|
| 76 |
-
{/if}
|
| 77 |
-
</a>
|
| 78 |
-
{/each}
|
| 79 |
-
<!-- if its huggingchat, the number of assistants owned by the user must be non-zero to show the UI -->
|
| 80 |
-
{#if data.enableAssistants}
|
| 81 |
-
<h3 bind:this={assistantsSection} class="pb-3 pl-3 pt-5 text-[.8rem] text-gray-800 sm:pl-1">
|
| 82 |
-
Assistants
|
| 83 |
-
</h3>
|
| 84 |
-
{#if !data.loginEnabled || (data.loginEnabled && !!data.user)}
|
| 85 |
-
<a
|
| 86 |
-
href="{base}/settings/assistants/new"
|
| 87 |
-
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl
|
| 88 |
-
{$page.url.pathname === `${base}/settings/assistants/new` ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 89 |
-
>
|
| 90 |
-
<CarbonAdd />
|
| 91 |
-
<div class="truncate">Create new assistant</div>
|
| 92 |
-
</a>
|
| 93 |
-
{/if}
|
| 94 |
-
{#each data.assistants as assistant}
|
| 95 |
-
<a
|
| 96 |
-
href="{base}/settings/assistants/{assistant._id.toString()}"
|
| 97 |
-
class="group flex h-10 flex-none items-center gap-2 pl-2 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl
|
| 98 |
-
{assistant._id.toString() === $page.params.assistantId ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 99 |
-
>
|
| 100 |
-
{#if assistant.avatar}
|
| 101 |
-
<img
|
| 102 |
-
src="{base}/settings/assistants/{assistant._id.toString()}/avatar.jpg?hash={assistant.avatar}"
|
| 103 |
-
alt="Avatar"
|
| 104 |
-
class="h-6 w-6 rounded-full object-cover"
|
| 105 |
-
/>
|
| 106 |
-
{:else}
|
| 107 |
-
<div
|
| 108 |
-
class="flex size-6 items-center justify-center rounded-full bg-gray-300 font-bold uppercase text-gray-500"
|
| 109 |
-
>
|
| 110 |
-
{assistant.name[0]}
|
| 111 |
-
</div>
|
| 112 |
-
{/if}
|
| 113 |
-
<div class="truncate">{assistant.name}</div>
|
| 114 |
-
{#if assistant._id.toString() === $settings.activeModel}
|
| 115 |
-
<div
|
| 116 |
-
class="ml-auto rounded-lg bg-black px-2 py-1.5 text-xs font-semibold leading-none text-white"
|
| 117 |
-
>
|
| 118 |
-
Active
|
| 119 |
-
</div>
|
| 120 |
-
{/if}
|
| 121 |
-
</a>
|
| 122 |
-
{/each}
|
| 123 |
-
<a
|
| 124 |
-
href="{base}/assistants"
|
| 125 |
-
class="group flex h-10 flex-none items-center gap-2 pl-3 pr-2 text-sm text-gray-500 hover:bg-gray-100 md:rounded-xl"
|
| 126 |
-
><CarbonArrowUpRight class="mr-1.5 shrink-0 text-xs " />
|
| 127 |
-
<div class="truncate">Browse Assistants</div>
|
| 128 |
-
</a>
|
| 129 |
-
{/if}
|
| 130 |
-
|
| 131 |
-
<a
|
| 132 |
-
href="{base}/settings"
|
| 133 |
-
class="group mt-auto 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
|
| 134 |
-
{$page.url.pathname === `${base}/settings` ? '!bg-gray-100 !text-gray-800' : ''}"
|
| 135 |
-
>
|
| 136 |
-
<UserIcon class="text-sm" />
|
| 137 |
-
Application Settings
|
| 138 |
-
</a>
|
| 139 |
-
</div>
|
| 140 |
-
<div class="col-span-1 overflow-y-auto px-4 max-md:-mx-4 max-md:pt-6 md:col-span-2">
|
| 141 |
-
<slot />
|
| 142 |
-
</div>
|
| 143 |
-
|
| 144 |
{#if $settings.recentlySaved}
|
| 145 |
<div
|
| 146 |
class="absolute bottom-4 right-4 m-2 flex items-center gap-1.5 rounded-full border border-gray-300 bg-gray-200 px-3 py-1 text-black"
|
|
|
|
| 1 |
<script lang="ts">
|
|
|
|
| 2 |
import { base } from "$app/paths";
|
| 3 |
import { clickOutside } from "$lib/actions/clickOutside";
|
| 4 |
import { afterNavigate, goto } from "$app/navigation";
|
|
|
|
| 5 |
import { useSettingsStore } from "$lib/stores/settings";
|
|
|
|
|
|
|
| 6 |
import CarbonCheckmark from "~icons/carbon/checkmark";
|
|
|
|
| 7 |
|
|
|
|
| 8 |
import { fade, fly } from "svelte/transition";
|
|
|
|
| 9 |
|
| 10 |
let previousPage: string = base;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
afterNavigate(({ from }) => {
|
| 13 |
if (!from?.url.pathname.includes("settings")) {
|
|
|
|
| 28 |
use:clickOutside={() => {
|
| 29 |
goto(previousPage);
|
| 30 |
}}
|
| 31 |
+
class="h-[95dvh] w-[90dvw] overflow-hidden rounded-2xl bg-white shadow-2xl outline-none sm:h-[80dvh] xl:w-[1200px] 2xl:h-[70dvh]"
|
| 32 |
>
|
| 33 |
+
<slot />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
{#if $settings.recentlySaved}
|
| 35 |
<div
|
| 36 |
class="absolute bottom-4 right-4 m-2 flex items-center gap-1.5 rounded-full border border-gray-300 bg-gray-200 px-3 py-1 text-black"
|