Spaces:
				
			
			
	
			
			
					
		Running
		
			on 
			
			CPU Upgrade
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
			on 
			
			CPU Upgrade
	
		Thomas G. Lopes
		
	commited on
		
		
					Commit 
							
							·
						
						936176c
	
1
								Parent(s):
							
							5ac02d2
								
migrate all?
Browse files- src/lib/components/avatar.svelte +7 -3
- src/lib/components/debug-menu.svelte +3 -3
- src/lib/components/icon-provider.svelte +8 -3
- src/lib/components/inference-playground/conversation.svelte +2 -0
- src/lib/components/inference-playground/hf-token-modal.svelte +14 -7
- src/lib/components/inference-playground/model-selector-modal.svelte +23 -17
- src/lib/components/inference-playground/model-selector.svelte +9 -5
- src/lib/components/inference-playground/playground.svelte +25 -21
- src/lib/components/inference-playground/provider-select.svelte +19 -9
- src/lib/components/prompts.svelte +16 -12
- src/routes/+layout.svelte +6 -1
    	
        src/lib/components/avatar.svelte
    CHANGED
    
    | @@ -1,8 +1,12 @@ | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            -
            	 | 
| 3 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
| 4 |  | 
| 5 | 
            -
            	 | 
| 6 |  | 
| 7 | 
             
            	async function getAvatarUrl(orgName?: string) {
         | 
| 8 | 
             
            		if (!orgName) return;
         | 
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            +
            	interface Props {
         | 
| 3 | 
            +
            		orgName: string | undefined;
         | 
| 4 | 
            +
            		size?: "sm" | "md";
         | 
| 5 | 
            +
            	}
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            	let { orgName, size = "md" }: Props = $props();
         | 
| 8 |  | 
| 9 | 
            +
            	let sizeClass = $derived(size === "sm" ? "size-3" : "size-4");
         | 
| 10 |  | 
| 11 | 
             
            	async function getAvatarUrl(orgName?: string) {
         | 
| 12 | 
             
            		if (!orgName) return;
         | 
    	
        src/lib/components/debug-menu.svelte
    CHANGED
    
    | @@ -8,8 +8,8 @@ | |
| 8 | 
             
            	import type { ToastData } from "./toaster.svelte.js";
         | 
| 9 | 
             
            	import { addToast } from "./toaster.svelte.js";
         | 
| 10 |  | 
| 11 | 
            -
            	let innerWidth | 
| 12 | 
            -
            	let innerHeight | 
| 13 |  | 
| 14 | 
             
            	function toggleTheme() {
         | 
| 15 | 
             
            		document.body.classList.toggle("dark");
         | 
| @@ -99,7 +99,7 @@ | |
| 99 | 
             
            					{#each actions as { label, cb }}
         | 
| 100 | 
             
            						<button
         | 
| 101 | 
             
            							class="rounded-md bg-gray-200 px-3 py-1 text-sm hover:bg-gray-300 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600"
         | 
| 102 | 
            -
            							 | 
| 103 | 
             
            						>
         | 
| 104 | 
             
            							{label}
         | 
| 105 | 
             
            						</button>
         | 
|  | |
| 8 | 
             
            	import type { ToastData } from "./toaster.svelte.js";
         | 
| 9 | 
             
            	import { addToast } from "./toaster.svelte.js";
         | 
| 10 |  | 
| 11 | 
            +
            	let innerWidth = $state<number>();
         | 
| 12 | 
            +
            	let innerHeight = $state<number>();
         | 
| 13 |  | 
| 14 | 
             
            	function toggleTheme() {
         | 
| 15 | 
             
            		document.body.classList.toggle("dark");
         | 
|  | |
| 99 | 
             
            					{#each actions as { label, cb }}
         | 
| 100 | 
             
            						<button
         | 
| 101 | 
             
            							class="rounded-md bg-gray-200 px-3 py-1 text-sm hover:bg-gray-300 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600"
         | 
| 102 | 
            +
            							onclick={cb}
         | 
| 103 | 
             
            						>
         | 
| 104 | 
             
            							{label}
         | 
| 105 | 
             
            						</button>
         | 
    	
        src/lib/components/icon-provider.svelte
    CHANGED
    
    | @@ -1,5 +1,10 @@ | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            -
            	 | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 3 | 
             
            </script>
         | 
| 4 |  | 
| 5 | 
             
            {#if provider === "sambanova"}
         | 
| @@ -294,7 +299,7 @@ | |
| 294 | 
             
            		></path></svg
         | 
| 295 | 
             
            	>
         | 
| 296 | 
             
            {:else}
         | 
| 297 | 
            -
            	 | 
| 298 | 
             
            		<div class="size-4 flex-none rounded-sm bg-gray-200"></div>
         | 
| 299 | 
            -
            	 | 
| 300 | 
             
            {/if}
         | 
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            +
            	interface Props {
         | 
| 3 | 
            +
            		provider: string | undefined;
         | 
| 4 | 
            +
            		children?: import('svelte').Snippet;
         | 
| 5 | 
            +
            	}
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            	let { provider, children }: Props = $props();
         | 
| 8 | 
             
            </script>
         | 
| 9 |  | 
| 10 | 
             
            {#if provider === "sambanova"}
         | 
|  | |
| 299 | 
             
            		></path></svg
         | 
| 300 | 
             
            	>
         | 
| 301 | 
             
            {:else}
         | 
| 302 | 
            +
            	{#if children}{@render children()}{:else}
         | 
| 303 | 
             
            		<div class="size-4 flex-none rounded-sm bg-gray-200"></div>
         | 
| 304 | 
            +
            	{/if}
         | 
| 305 | 
             
            {/if}
         | 
    	
        src/lib/components/inference-playground/conversation.svelte
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
|  | |
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
             
            	import { run } from "svelte/legacy";
         | 
| 3 |  | 
|  | |
| 1 | 
            +
            <!-- @migration-task Error while migrating Svelte code: Can only bind to an Identifier or MemberExpression or a `{get, set}` pair
         | 
| 2 | 
            +
            https://svelte.dev/e/bind_invalid_expression -->
         | 
| 3 | 
             
            <script lang="ts">
         | 
| 4 | 
             
            	import { run } from "svelte/legacy";
         | 
| 5 |  | 
    	
        src/lib/components/inference-playground/hf-token-modal.svelte
    CHANGED
    
    | @@ -1,13 +1,20 @@ | |
| 1 | 
             
            <script lang="ts">
         | 
|  | |
|  | |
|  | |
| 2 | 
             
            	import { clickOutside } from "$lib/actions/click-outside.js";
         | 
| 3 | 
             
            	import { createEventDispatcher, onDestroy, onMount } from "svelte";
         | 
| 4 |  | 
| 5 | 
             
            	import IconCross from "~icons/carbon/close";
         | 
| 6 |  | 
| 7 | 
            -
            	 | 
|  | |
|  | |
|  | |
|  | |
| 8 |  | 
| 9 | 
            -
            	let backdropEl | 
| 10 | 
            -
            	let modalEl | 
| 11 |  | 
| 12 | 
             
            	const dispatch = createEventDispatcher<{ close: void }>();
         | 
| 13 |  | 
| @@ -21,7 +28,7 @@ | |
| 21 |  | 
| 22 | 
             
            	onMount(() => {
         | 
| 23 | 
             
            		document.getElementById("app")?.setAttribute("inert", "true");
         | 
| 24 | 
            -
            		modalEl | 
| 25 | 
             
            	});
         | 
| 26 |  | 
| 27 | 
             
            	onDestroy(() => {
         | 
| @@ -43,10 +50,10 @@ | |
| 43 | 
             
            		tabindex="-1"
         | 
| 44 | 
             
            		class="relative max-h-full w-full max-w-xl p-4 outline-hidden"
         | 
| 45 | 
             
            		bind:this={modalEl}
         | 
| 46 | 
            -
            		 | 
| 47 | 
             
            		use:clickOutside={() => dispatch("close")}
         | 
| 48 | 
             
            	>
         | 
| 49 | 
            -
            		<form  | 
| 50 | 
             
            			<div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-800">
         | 
| 51 | 
             
            				<h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white">
         | 
| 52 | 
             
            					<img
         | 
| @@ -57,7 +64,7 @@ | |
| 57 | 
             
            				</h3>
         | 
| 58 | 
             
            				<button
         | 
| 59 | 
             
            					type="button"
         | 
| 60 | 
            -
            					 | 
| 61 | 
             
            					class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
         | 
| 62 | 
             
            				>
         | 
| 63 | 
             
            					<div class="text-xl">
         | 
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            +
            	import { createBubbler, preventDefault } from "svelte/legacy";
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            	const bubble = createBubbler();
         | 
| 5 | 
             
            	import { clickOutside } from "$lib/actions/click-outside.js";
         | 
| 6 | 
             
            	import { createEventDispatcher, onDestroy, onMount } from "svelte";
         | 
| 7 |  | 
| 8 | 
             
            	import IconCross from "~icons/carbon/close";
         | 
| 9 |  | 
| 10 | 
            +
            	interface Props {
         | 
| 11 | 
            +
            		storeLocallyHfToken?: boolean;
         | 
| 12 | 
            +
            	}
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            	let { storeLocallyHfToken = $bindable(false) }: Props = $props();
         | 
| 15 |  | 
| 16 | 
            +
            	let backdropEl = $state<HTMLDivElement>();
         | 
| 17 | 
            +
            	let modalEl = $state<HTMLDivElement>();
         | 
| 18 |  | 
| 19 | 
             
            	const dispatch = createEventDispatcher<{ close: void }>();
         | 
| 20 |  | 
|  | |
| 28 |  | 
| 29 | 
             
            	onMount(() => {
         | 
| 30 | 
             
            		document.getElementById("app")?.setAttribute("inert", "true");
         | 
| 31 | 
            +
            		modalEl?.focus();
         | 
| 32 | 
             
            	});
         | 
| 33 |  | 
| 34 | 
             
            	onDestroy(() => {
         | 
|  | |
| 50 | 
             
            		tabindex="-1"
         | 
| 51 | 
             
            		class="relative max-h-full w-full max-w-xl p-4 outline-hidden"
         | 
| 52 | 
             
            		bind:this={modalEl}
         | 
| 53 | 
            +
            		onkeydown={handleKeydown}
         | 
| 54 | 
             
            		use:clickOutside={() => dispatch("close")}
         | 
| 55 | 
             
            	>
         | 
| 56 | 
            +
            		<form onsubmit={preventDefault(bubble("submit"))} class="relative rounded-lg bg-white shadow-sm dark:bg-gray-900">
         | 
| 57 | 
             
            			<div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-800">
         | 
| 58 | 
             
            				<h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white">
         | 
| 59 | 
             
            					<img
         | 
|  | |
| 64 | 
             
            				</h3>
         | 
| 65 | 
             
            				<button
         | 
| 66 | 
             
            					type="button"
         | 
| 67 | 
            +
            					onclick={() => dispatch("close")}
         | 
| 68 | 
             
            					class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
         | 
| 69 | 
             
            				>
         | 
| 70 | 
             
            					<div class="text-xl">
         | 
    	
        src/lib/components/inference-playground/model-selector-modal.svelte
    CHANGED
    
    | @@ -9,20 +9,24 @@ | |
| 9 | 
             
            	import IconSearch from "~icons/carbon/search";
         | 
| 10 | 
             
            	import IconStar from "~icons/carbon/star";
         | 
| 11 |  | 
| 12 | 
            -
            	 | 
|  | |
|  | |
|  | |
|  | |
| 13 |  | 
| 14 | 
            -
            	let backdropEl | 
| 15 | 
            -
            	let highlightIdx = 0;
         | 
| 16 | 
            -
            	let ignoreCursorHighlight = false;
         | 
| 17 | 
            -
            	let containerEl | 
| 18 | 
            -
            	let query = "";
         | 
| 19 |  | 
| 20 | 
             
            	const dispatch = createEventDispatcher<{ modelSelected: string; close: void }>();
         | 
| 21 |  | 
| 22 | 
            -
            	 | 
| 23 |  | 
| 24 | 
            -
            	 | 
| 25 | 
            -
            	 | 
| 26 |  | 
| 27 | 
             
            	onMount(() => {
         | 
| 28 | 
             
            		if (featuredModels.findIndex(model => model.id === conversation.model.id) !== -1) {
         | 
| @@ -80,6 +84,7 @@ | |
| 80 | 
             
            	}
         | 
| 81 |  | 
| 82 | 
             
            	function handleBackdropClick(event: MouseEvent) {
         | 
|  | |
| 83 | 
             
            		if (window?.getSelection()?.toString()) {
         | 
| 84 | 
             
            			return;
         | 
| 85 | 
             
            		}
         | 
| @@ -89,13 +94,14 @@ | |
| 89 | 
             
            	}
         | 
| 90 | 
             
            </script>
         | 
| 91 |  | 
| 92 | 
            -
            <svelte:window  | 
| 93 |  | 
| 94 | 
            -
            <!-- svelte-ignore  | 
|  | |
| 95 | 
             
            <div
         | 
| 96 | 
             
            	class="fixed inset-0 z-10 flex h-screen items-start justify-center bg-black/85 pt-32"
         | 
| 97 | 
             
            	bind:this={backdropEl}
         | 
| 98 | 
            -
            	 | 
| 99 | 
             
            >
         | 
| 100 | 
             
            	<div class="flex w-full max-w-[600px] items-start justify-center overflow-hidden p-10 text-left whitespace-nowrap">
         | 
| 101 | 
             
            		<div
         | 
| @@ -106,7 +112,7 @@ | |
| 106 | 
             
            				<div class="mr-2 text-sm">
         | 
| 107 | 
             
            					<IconSearch />
         | 
| 108 | 
             
            				</div>
         | 
| 109 | 
            -
            				<!-- svelte-ignore  | 
| 110 | 
             
            				<input
         | 
| 111 | 
             
            					autofocus
         | 
| 112 | 
             
            					class="flex h-10 w-full rounded-md bg-transparent py-3 text-sm placeholder-gray-400 outline-hidden"
         | 
| @@ -125,8 +131,8 @@ | |
| 125 | 
             
            									class="flex w-full cursor-pointer items-center px-2 py-1.5 text-sm {highlightIdx === idx
         | 
| 126 | 
             
            										? 'highlighted bg-gray-100 dark:bg-gray-800'
         | 
| 127 | 
             
            										: ''}"
         | 
| 128 | 
            -
            									 | 
| 129 | 
            -
            									 | 
| 130 | 
             
            										dispatch("modelSelected", model.id);
         | 
| 131 | 
             
            										dispatch("close");
         | 
| 132 | 
             
            									}}
         | 
| @@ -155,8 +161,8 @@ | |
| 155 | 
             
            									class="flex w-full cursor-pointer items-center px-2 py-1.5 text-sm {highlightIdx === idx
         | 
| 156 | 
             
            										? 'highlighted bg-gray-100 dark:bg-gray-800'
         | 
| 157 | 
             
            										: ''}"
         | 
| 158 | 
            -
            									 | 
| 159 | 
            -
            									 | 
| 160 | 
             
            										dispatch("modelSelected", model.id);
         | 
| 161 | 
             
            										dispatch("close");
         | 
| 162 | 
             
            									}}
         | 
|  | |
| 9 | 
             
            	import IconSearch from "~icons/carbon/search";
         | 
| 10 | 
             
            	import IconStar from "~icons/carbon/star";
         | 
| 11 |  | 
| 12 | 
            +
            	interface Props {
         | 
| 13 | 
            +
            		conversation: Conversation;
         | 
| 14 | 
            +
            	}
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            	let { conversation }: Props = $props();
         | 
| 17 |  | 
| 18 | 
            +
            	let backdropEl = $state<HTMLDivElement>();
         | 
| 19 | 
            +
            	let highlightIdx = $state(0);
         | 
| 20 | 
            +
            	let ignoreCursorHighlight = $state(false);
         | 
| 21 | 
            +
            	let containerEl = $state<HTMLDivElement>();
         | 
| 22 | 
            +
            	let query = $state("");
         | 
| 23 |  | 
| 24 | 
             
            	const dispatch = createEventDispatcher<{ modelSelected: string; close: void }>();
         | 
| 25 |  | 
| 26 | 
            +
            	let trendingModels = $derived(getTrending($models));
         | 
| 27 |  | 
| 28 | 
            +
            	let featuredModels = $derived(fuzzysearch({ needle: query, haystack: trendingModels, property: "id" }));
         | 
| 29 | 
            +
            	let otherModels = $derived(fuzzysearch({ needle: query, haystack: $models, property: "id" }));
         | 
| 30 |  | 
| 31 | 
             
            	onMount(() => {
         | 
| 32 | 
             
            		if (featuredModels.findIndex(model => model.id === conversation.model.id) !== -1) {
         | 
|  | |
| 84 | 
             
            	}
         | 
| 85 |  | 
| 86 | 
             
            	function handleBackdropClick(event: MouseEvent) {
         | 
| 87 | 
            +
            		event.stopPropagation();
         | 
| 88 | 
             
            		if (window?.getSelection()?.toString()) {
         | 
| 89 | 
             
            			return;
         | 
| 90 | 
             
            		}
         | 
|  | |
| 94 | 
             
            	}
         | 
| 95 | 
             
            </script>
         | 
| 96 |  | 
| 97 | 
            +
            <svelte:window onkeydown={handleKeydown} onmousemove={() => (ignoreCursorHighlight = false)} />
         | 
| 98 |  | 
| 99 | 
            +
            <!-- svelte-ignore a11y_no_static_element_interactions -->
         | 
| 100 | 
            +
            <!-- svelte-ignore a11y_click_events_have_key_events -->
         | 
| 101 | 
             
            <div
         | 
| 102 | 
             
            	class="fixed inset-0 z-10 flex h-screen items-start justify-center bg-black/85 pt-32"
         | 
| 103 | 
             
            	bind:this={backdropEl}
         | 
| 104 | 
            +
            	onclick={handleBackdropClick}
         | 
| 105 | 
             
            >
         | 
| 106 | 
             
            	<div class="flex w-full max-w-[600px] items-start justify-center overflow-hidden p-10 text-left whitespace-nowrap">
         | 
| 107 | 
             
            		<div
         | 
|  | |
| 112 | 
             
            				<div class="mr-2 text-sm">
         | 
| 113 | 
             
            					<IconSearch />
         | 
| 114 | 
             
            				</div>
         | 
| 115 | 
            +
            				<!-- svelte-ignore a11y_autofocus -->
         | 
| 116 | 
             
            				<input
         | 
| 117 | 
             
            					autofocus
         | 
| 118 | 
             
            					class="flex h-10 w-full rounded-md bg-transparent py-3 text-sm placeholder-gray-400 outline-hidden"
         | 
|  | |
| 131 | 
             
            									class="flex w-full cursor-pointer items-center px-2 py-1.5 text-sm {highlightIdx === idx
         | 
| 132 | 
             
            										? 'highlighted bg-gray-100 dark:bg-gray-800'
         | 
| 133 | 
             
            										: ''}"
         | 
| 134 | 
            +
            									onmouseenter={() => highlightRow(idx)}
         | 
| 135 | 
            +
            									onclick={() => {
         | 
| 136 | 
             
            										dispatch("modelSelected", model.id);
         | 
| 137 | 
             
            										dispatch("close");
         | 
| 138 | 
             
            									}}
         | 
|  | |
| 161 | 
             
            									class="flex w-full cursor-pointer items-center px-2 py-1.5 text-sm {highlightIdx === idx
         | 
| 162 | 
             
            										? 'highlighted bg-gray-100 dark:bg-gray-800'
         | 
| 163 | 
             
            										: ''}"
         | 
| 164 | 
            +
            									onmouseenter={() => highlightRow(idx)}
         | 
| 165 | 
            +
            									onclick={() => {
         | 
| 166 | 
             
            										dispatch("modelSelected", model.id);
         | 
| 167 | 
             
            										dispatch("close");
         | 
| 168 | 
             
            									}}
         | 
    	
        src/lib/components/inference-playground/model-selector.svelte
    CHANGED
    
    | @@ -8,9 +8,13 @@ | |
| 8 | 
             
            	import ProviderSelect from "./provider-select.svelte";
         | 
| 9 | 
             
            	import { defaultSystemMessage } from "./utils.js";
         | 
| 10 |  | 
| 11 | 
            -
            	 | 
|  | |
|  | |
|  | |
|  | |
| 12 |  | 
| 13 | 
            -
            	let showModelPickerModal = false;
         | 
| 14 |  | 
| 15 | 
             
            	// Model
         | 
| 16 | 
             
            	function changeModel(modelId: ModelWithTokenizer["id"]) {
         | 
| @@ -23,8 +27,8 @@ | |
| 23 | 
             
            		conversation.provider = undefined;
         | 
| 24 | 
             
            	}
         | 
| 25 |  | 
| 26 | 
            -
            	 | 
| 27 | 
            -
            	 | 
| 28 | 
             
            	const id = crypto.randomUUID();
         | 
| 29 | 
             
            </script>
         | 
| 30 |  | 
| @@ -36,7 +40,7 @@ | |
| 36 | 
             
            	<button
         | 
| 37 | 
             
            		{id}
         | 
| 38 | 
             
            		class="relative flex items-center justify-between gap-6 overflow-hidden rounded-lg border bg-gray-100/80 px-3 py-1.5 leading-tight whitespace-nowrap shadow-sm hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110"
         | 
| 39 | 
            -
            		 | 
| 40 | 
             
            	>
         | 
| 41 | 
             
            		<div class="flex flex-col items-start">
         | 
| 42 | 
             
            			<div class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-300">
         | 
|  | |
| 8 | 
             
            	import ProviderSelect from "./provider-select.svelte";
         | 
| 9 | 
             
            	import { defaultSystemMessage } from "./utils.js";
         | 
| 10 |  | 
| 11 | 
            +
            	interface Props {
         | 
| 12 | 
            +
            		conversation: Conversation;
         | 
| 13 | 
            +
            	}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            	let { conversation = $bindable() }: Props = $props();
         | 
| 16 |  | 
| 17 | 
            +
            	let showModelPickerModal = $state(false);
         | 
| 18 |  | 
| 19 | 
             
            	// Model
         | 
| 20 | 
             
            	function changeModel(modelId: ModelWithTokenizer["id"]) {
         | 
|  | |
| 27 | 
             
            		conversation.provider = undefined;
         | 
| 28 | 
             
            	}
         | 
| 29 |  | 
| 30 | 
            +
            	let nameSpace = $derived(conversation.model.id.split("/")[0] ?? "");
         | 
| 31 | 
            +
            	let modelName = $derived(conversation.model.id.split("/")[1] ?? "");
         | 
| 32 | 
             
            	const id = crypto.randomUUID();
         | 
| 33 | 
             
            </script>
         | 
| 34 |  | 
|  | |
| 40 | 
             
            	<button
         | 
| 41 | 
             
            		{id}
         | 
| 42 | 
             
            		class="relative flex items-center justify-between gap-6 overflow-hidden rounded-lg border bg-gray-100/80 px-3 py-1.5 leading-tight whitespace-nowrap shadow-sm hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110"
         | 
| 43 | 
            +
            		onclick={() => (showModelPickerModal = true)}
         | 
| 44 | 
             
            	>
         | 
| 45 | 
             
            		<div class="flex flex-col items-start">
         | 
| 46 | 
             
            			<div class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-300">
         | 
    	
        src/lib/components/inference-playground/playground.svelte
    CHANGED
    
    | @@ -25,23 +25,27 @@ | |
| 25 |  | 
| 26 | 
             
            	const startMessageUser: ConversationMessage = { role: "user", content: "" };
         | 
| 27 |  | 
| 28 | 
            -
            	let viewCode = false;
         | 
| 29 | 
            -
            	let viewSettings = false;
         | 
| 30 | 
            -
            	let loading = false;
         | 
| 31 | 
             
            	let abortControllers: AbortController[] = [];
         | 
| 32 | 
             
            	let waitForNonStreaming = true;
         | 
| 33 | 
            -
            	let selectCompareModelOpen = false;
         | 
| 34 |  | 
| 35 | 
             
            	interface GenerationStatistics {
         | 
| 36 | 
             
            		latency: number;
         | 
| 37 | 
             
            		generatedTokensCount: number;
         | 
| 38 | 
             
            	}
         | 
| 39 | 
            -
            	let generationStats = $ | 
| 40 | 
            -
            		 | 
| 41 | 
            -
             | 
|  | |
|  | |
| 42 |  | 
| 43 | 
            -
            	 | 
| 44 | 
            -
             | 
|  | |
|  | |
| 45 |  | 
| 46 | 
             
            	function reset() {
         | 
| 47 | 
             
            		$project.conversations.map(conversation => {
         | 
| @@ -209,7 +213,7 @@ | |
| 209 | 
             
            	/>
         | 
| 210 | 
             
            {/if}
         | 
| 211 |  | 
| 212 | 
            -
            <!-- svelte-ignore  | 
| 213 | 
             
            <div
         | 
| 214 | 
             
            	class="motion-safe:animate-fade-in grid h-dvh divide-gray-200 overflow-hidden bg-gray-100/50 max-md:grid-rows-[120px_1fr] max-md:divide-y dark:divide-gray-800 dark:bg-gray-900 dark:text-gray-300 dark:[color-scheme:dark] {compareActive
         | 
| 215 | 
             
            		? 'md:grid-cols-[clamp(220px,20%,350px)_minmax(0,1fr)]'
         | 
| @@ -232,7 +236,7 @@ | |
| 232 | 
             
            					? "Enter a custom prompt"
         | 
| 233 | 
             
            					: "System prompt is not supported with the chosen model."}
         | 
| 234 | 
             
            				value={systemPromptSupported ? $project.conversations[0].systemMessage.content : ""}
         | 
| 235 | 
            -
            				 | 
| 236 | 
             
            					for (const conversation of $project.conversations) {
         | 
| 237 | 
             
            						conversation.systemMessage.content = e.currentTarget.value;
         | 
| 238 | 
             
            					}
         | 
| @@ -242,22 +246,22 @@ | |
| 242 | 
             
            			></textarea>
         | 
| 243 | 
             
            		</div>
         | 
| 244 | 
             
            	</div>
         | 
| 245 | 
            -
            	<div class="relative divide-y divide-gray-200 dark:divide-gray-800"  | 
| 246 | 
             
            		<div
         | 
| 247 | 
             
            			class="flex h-[calc(100dvh-5rem-120px)] divide-x divide-gray-200 overflow-x-auto overflow-y-hidden *:w-full max-sm:w-dvw md:h-[calc(100dvh-5rem)] md:pt-3 dark:divide-gray-800"
         | 
| 248 | 
             
            		>
         | 
| 249 | 
            -
            			{#each $project.conversations as  | 
| 250 | 
             
            				<div class="max-sm:min-w-full">
         | 
| 251 | 
             
            					{#if compareActive}
         | 
| 252 | 
             
            						<PlaygroundConversationHeader
         | 
| 253 | 
             
            							{conversationIdx}
         | 
| 254 | 
            -
            							bind:conversation
         | 
| 255 | 
             
            							on:close={() => removeCompareModal(conversationIdx)}
         | 
| 256 | 
             
            						/>
         | 
| 257 | 
             
            					{/if}
         | 
| 258 | 
             
            					<PlaygroundConversation
         | 
| 259 | 
             
            						{loading}
         | 
| 260 | 
            -
            						bind:conversation
         | 
| 261 | 
             
            						{viewCode}
         | 
| 262 | 
             
            						{compareActive}
         | 
| 263 | 
             
            						on:closeCode={() => (viewCode = false)}
         | 
| @@ -272,7 +276,7 @@ | |
| 272 | 
             
            				{#if !compareActive}
         | 
| 273 | 
             
            					<button
         | 
| 274 | 
             
            						type="button"
         | 
| 275 | 
            -
            						 | 
| 276 | 
             
            						class="flex h-[39px] items-center gap-1 rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden md:hidden dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
         | 
| 277 | 
             
            					>
         | 
| 278 | 
             
            						<div class="text-black dark:text-white">
         | 
| @@ -281,7 +285,7 @@ | |
| 281 | 
             
            						{!viewSettings ? "Settings" : "Hide Settings"}
         | 
| 282 | 
             
            					</button>
         | 
| 283 | 
             
            				{/if}
         | 
| 284 | 
            -
            				<button type="button"  | 
| 285 | 
             
            					<IconDelete />
         | 
| 286 | 
             
            				</button>
         | 
| 287 | 
             
            			</div>
         | 
| @@ -291,12 +295,12 @@ | |
| 291 | 
             
            				{/each}
         | 
| 292 | 
             
            			</div>
         | 
| 293 | 
             
            			<div class="flex flex-1 justify-end gap-x-2">
         | 
| 294 | 
            -
            				<button type="button"  | 
| 295 | 
             
            					<IconCode />
         | 
| 296 | 
             
            					{!viewCode ? "View Code" : "Hide Code"}</button
         | 
| 297 | 
             
            				>
         | 
| 298 | 
             
            				<button
         | 
| 299 | 
            -
            					 | 
| 300 | 
             
            						viewCode = false;
         | 
| 301 | 
             
            						loading ? abort() : submit();
         | 
| 302 | 
             
            					}}
         | 
| @@ -347,7 +351,7 @@ | |
| 347 | 
             
            					<div class="flex items-center gap-2 self-end px-2 text-xs whitespace-nowrap">
         | 
| 348 | 
             
            						<button
         | 
| 349 | 
             
            							class="flex items-center gap-0.5 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
         | 
| 350 | 
            -
            							 | 
| 351 | 
             
            						>
         | 
| 352 | 
             
            							<IconCompare />
         | 
| 353 | 
             
            							Compare
         | 
| @@ -367,7 +371,7 @@ | |
| 367 | 
             
            				<GenerationConfig bind:conversation={$project.conversations[0]} />
         | 
| 368 | 
             
            				{#if $token.value}
         | 
| 369 | 
             
            					<button
         | 
| 370 | 
            -
            						 | 
| 371 | 
             
            						class="mt-auto flex items-center gap-1 self-end text-sm text-gray-500 underline decoration-gray-300 hover:text-gray-800 dark:text-gray-400 dark:decoration-gray-600 dark:hover:text-gray-200"
         | 
| 372 | 
             
            						><svg xmlns="http://www.w3.org/2000/svg" class="text-xs" width="1em" height="1em" viewBox="0 0 32 32"
         | 
| 373 | 
             
            							><path
         | 
|  | |
| 25 |  | 
| 26 | 
             
            	const startMessageUser: ConversationMessage = { role: "user", content: "" };
         | 
| 27 |  | 
| 28 | 
            +
            	let viewCode = $state(false);
         | 
| 29 | 
            +
            	let viewSettings = $state(false);
         | 
| 30 | 
            +
            	let loading = $state(false);
         | 
| 31 | 
             
            	let abortControllers: AbortController[] = [];
         | 
| 32 | 
             
            	let waitForNonStreaming = true;
         | 
| 33 | 
            +
            	let selectCompareModelOpen = $state(false);
         | 
| 34 |  | 
| 35 | 
             
            	interface GenerationStatistics {
         | 
| 36 | 
             
            		latency: number;
         | 
| 37 | 
             
            		generatedTokensCount: number;
         | 
| 38 | 
             
            	}
         | 
| 39 | 
            +
            	let generationStats = $state(
         | 
| 40 | 
            +
            		$project.conversations.map(_ => ({ latency: 0, generatedTokensCount: 0 })) as
         | 
| 41 | 
            +
            			| [GenerationStatistics]
         | 
| 42 | 
            +
            			| [GenerationStatistics, GenerationStatistics]
         | 
| 43 | 
            +
            	);
         | 
| 44 |  | 
| 45 | 
            +
            	let systemPromptSupported = $derived(
         | 
| 46 | 
            +
            		$project.conversations.some(conversation => isSystemPromptSupported(conversation.model))
         | 
| 47 | 
            +
            	);
         | 
| 48 | 
            +
            	let compareActive = $derived($project.conversations.length === 2);
         | 
| 49 |  | 
| 50 | 
             
            	function reset() {
         | 
| 51 | 
             
            		$project.conversations.map(conversation => {
         | 
|  | |
| 213 | 
             
            	/>
         | 
| 214 | 
             
            {/if}
         | 
| 215 |  | 
| 216 | 
            +
            <!-- svelte-ignore a11y_no_static_element_interactions -->
         | 
| 217 | 
             
            <div
         | 
| 218 | 
             
            	class="motion-safe:animate-fade-in grid h-dvh divide-gray-200 overflow-hidden bg-gray-100/50 max-md:grid-rows-[120px_1fr] max-md:divide-y dark:divide-gray-800 dark:bg-gray-900 dark:text-gray-300 dark:[color-scheme:dark] {compareActive
         | 
| 219 | 
             
            		? 'md:grid-cols-[clamp(220px,20%,350px)_minmax(0,1fr)]'
         | 
|  | |
| 236 | 
             
            					? "Enter a custom prompt"
         | 
| 237 | 
             
            					: "System prompt is not supported with the chosen model."}
         | 
| 238 | 
             
            				value={systemPromptSupported ? $project.conversations[0].systemMessage.content : ""}
         | 
| 239 | 
            +
            				oninput={e => {
         | 
| 240 | 
             
            					for (const conversation of $project.conversations) {
         | 
| 241 | 
             
            						conversation.systemMessage.content = e.currentTarget.value;
         | 
| 242 | 
             
            					}
         | 
|  | |
| 246 | 
             
            			></textarea>
         | 
| 247 | 
             
            		</div>
         | 
| 248 | 
             
            	</div>
         | 
| 249 | 
            +
            	<div class="relative divide-y divide-gray-200 dark:divide-gray-800" onkeydown={onKeydown}>
         | 
| 250 | 
             
            		<div
         | 
| 251 | 
             
            			class="flex h-[calc(100dvh-5rem-120px)] divide-x divide-gray-200 overflow-x-auto overflow-y-hidden *:w-full max-sm:w-dvw md:h-[calc(100dvh-5rem)] md:pt-3 dark:divide-gray-800"
         | 
| 252 | 
             
            		>
         | 
| 253 | 
            +
            			{#each $project.conversations as _conversation, conversationIdx}
         | 
| 254 | 
             
            				<div class="max-sm:min-w-full">
         | 
| 255 | 
             
            					{#if compareActive}
         | 
| 256 | 
             
            						<PlaygroundConversationHeader
         | 
| 257 | 
             
            							{conversationIdx}
         | 
| 258 | 
            +
            							bind:conversation={$project.conversations[conversationIdx]!}
         | 
| 259 | 
             
            							on:close={() => removeCompareModal(conversationIdx)}
         | 
| 260 | 
             
            						/>
         | 
| 261 | 
             
            					{/if}
         | 
| 262 | 
             
            					<PlaygroundConversation
         | 
| 263 | 
             
            						{loading}
         | 
| 264 | 
            +
            						bind:conversation={$project.conversations[conversationIdx]!}
         | 
| 265 | 
             
            						{viewCode}
         | 
| 266 | 
             
            						{compareActive}
         | 
| 267 | 
             
            						on:closeCode={() => (viewCode = false)}
         | 
|  | |
| 276 | 
             
            				{#if !compareActive}
         | 
| 277 | 
             
            					<button
         | 
| 278 | 
             
            						type="button"
         | 
| 279 | 
            +
            						onclick={() => (viewSettings = !viewSettings)}
         | 
| 280 | 
             
            						class="flex h-[39px] items-center gap-1 rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden md:hidden dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
         | 
| 281 | 
             
            					>
         | 
| 282 | 
             
            						<div class="text-black dark:text-white">
         | 
|  | |
| 285 | 
             
            						{!viewSettings ? "Settings" : "Hide Settings"}
         | 
| 286 | 
             
            					</button>
         | 
| 287 | 
             
            				{/if}
         | 
| 288 | 
            +
            				<button type="button" onclick={reset} class="btn size-[39px]">
         | 
| 289 | 
             
            					<IconDelete />
         | 
| 290 | 
             
            				</button>
         | 
| 291 | 
             
            			</div>
         | 
|  | |
| 295 | 
             
            				{/each}
         | 
| 296 | 
             
            			</div>
         | 
| 297 | 
             
            			<div class="flex flex-1 justify-end gap-x-2">
         | 
| 298 | 
            +
            				<button type="button" onclick={() => (viewCode = !viewCode)} class="btn">
         | 
| 299 | 
             
            					<IconCode />
         | 
| 300 | 
             
            					{!viewCode ? "View Code" : "Hide Code"}</button
         | 
| 301 | 
             
            				>
         | 
| 302 | 
             
            				<button
         | 
| 303 | 
            +
            					onclick={() => {
         | 
| 304 | 
             
            						viewCode = false;
         | 
| 305 | 
             
            						loading ? abort() : submit();
         | 
| 306 | 
             
            					}}
         | 
|  | |
| 351 | 
             
            					<div class="flex items-center gap-2 self-end px-2 text-xs whitespace-nowrap">
         | 
| 352 | 
             
            						<button
         | 
| 353 | 
             
            							class="flex items-center gap-0.5 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
         | 
| 354 | 
            +
            							onclick={() => (selectCompareModelOpen = true)}
         | 
| 355 | 
             
            						>
         | 
| 356 | 
             
            							<IconCompare />
         | 
| 357 | 
             
            							Compare
         | 
|  | |
| 371 | 
             
            				<GenerationConfig bind:conversation={$project.conversations[0]} />
         | 
| 372 | 
             
            				{#if $token.value}
         | 
| 373 | 
             
            					<button
         | 
| 374 | 
            +
            						onclick={token.reset}
         | 
| 375 | 
             
            						class="mt-auto flex items-center gap-1 self-end text-sm text-gray-500 underline decoration-gray-300 hover:text-gray-800 dark:text-gray-400 dark:decoration-gray-600 dark:hover:text-gray-200"
         | 
| 376 | 
             
            						><svg xmlns="http://www.w3.org/2000/svg" class="text-xs" width="1em" height="1em" viewBox="0 0 32 32"
         | 
| 377 | 
             
            							><path
         | 
    	
        src/lib/components/inference-playground/provider-select.svelte
    CHANGED
    
    | @@ -1,4 +1,6 @@ | |
| 1 | 
             
            <script lang="ts">
         | 
|  | |
|  | |
| 2 | 
             
            	import type { Conversation } from "$lib/types.js";
         | 
| 3 |  | 
| 4 | 
             
            	import { randomPick } from "$lib/utils/array.js";
         | 
| @@ -7,9 +9,13 @@ | |
| 7 | 
             
            	import IconCaret from "~icons/carbon/chevron-down";
         | 
| 8 | 
             
            	import IconProvider from "../icon-provider.svelte";
         | 
| 9 |  | 
| 10 | 
            -
            	 | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
| 13 |  | 
| 14 | 
             
            	function reset(providers: typeof conversation.model.inferenceProviderMapping) {
         | 
| 15 | 
             
            		const validProvider = providers.find(p => p.provider === conversation.provider);
         | 
| @@ -17,18 +23,22 @@ | |
| 17 | 
             
            		conversation.provider = randomPick(providers)?.provider;
         | 
| 18 | 
             
            	}
         | 
| 19 |  | 
| 20 | 
            -
            	 | 
| 21 | 
            -
            	 | 
|  | |
|  | |
| 22 |  | 
| 23 | 
             
            	const {
         | 
| 24 | 
             
            		elements: { trigger, menu, option },
         | 
| 25 | 
             
            		states: { selected },
         | 
| 26 | 
             
            	} = createSelect<string, false>();
         | 
| 27 | 
             
            	const sync = createSync({ selected });
         | 
| 28 | 
            -
            	 | 
| 29 | 
            -
            		 | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
|  | |
|  | |
| 32 |  | 
| 33 | 
             
            	const nameMap: Record<string, string> = {
         | 
| 34 | 
             
            		"sambanova": "SambaNova",
         | 
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            +
            	import { run } from 'svelte/legacy';
         | 
| 3 | 
            +
             | 
| 4 | 
             
            	import type { Conversation } from "$lib/types.js";
         | 
| 5 |  | 
| 6 | 
             
            	import { randomPick } from "$lib/utils/array.js";
         | 
|  | |
| 9 | 
             
            	import IconCaret from "~icons/carbon/chevron-down";
         | 
| 10 | 
             
            	import IconProvider from "../icon-provider.svelte";
         | 
| 11 |  | 
| 12 | 
            +
            	interface Props {
         | 
| 13 | 
            +
            		conversation: Conversation;
         | 
| 14 | 
            +
            		class?: string | undefined;
         | 
| 15 | 
            +
            	}
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            	let { conversation = $bindable(), class: classes = undefined }: Props = $props();
         | 
| 18 | 
            +
            	
         | 
| 19 |  | 
| 20 | 
             
            	function reset(providers: typeof conversation.model.inferenceProviderMapping) {
         | 
| 21 | 
             
            		const validProvider = providers.find(p => p.provider === conversation.provider);
         | 
|  | |
| 23 | 
             
            		conversation.provider = randomPick(providers)?.provider;
         | 
| 24 | 
             
            	}
         | 
| 25 |  | 
| 26 | 
            +
            	let providers = $derived(conversation.model.inferenceProviderMapping);
         | 
| 27 | 
            +
            	run(() => {
         | 
| 28 | 
            +
            		reset(providers);
         | 
| 29 | 
            +
            	});
         | 
| 30 |  | 
| 31 | 
             
            	const {
         | 
| 32 | 
             
            		elements: { trigger, menu, option },
         | 
| 33 | 
             
            		states: { selected },
         | 
| 34 | 
             
            	} = createSelect<string, false>();
         | 
| 35 | 
             
            	const sync = createSync({ selected });
         | 
| 36 | 
            +
            	run(() => {
         | 
| 37 | 
            +
            		sync.selected(
         | 
| 38 | 
            +
            			conversation.provider ? { value: conversation.provider } : undefined,
         | 
| 39 | 
            +
            			p => (conversation.provider = p?.value)
         | 
| 40 | 
            +
            		);
         | 
| 41 | 
            +
            	});
         | 
| 42 |  | 
| 43 | 
             
            	const nameMap: Record<string, string> = {
         | 
| 44 | 
             
            		"sambanova": "SambaNova",
         | 
    	
        src/lib/components/prompts.svelte
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            <script lang="ts"  | 
| 2 | 
             
            	import { clickOutside } from "$lib/actions/click-outside.js";
         | 
| 3 | 
             
            	import { writable } from "svelte/store";
         | 
| 4 | 
             
            	import IconCross from "~icons/carbon/close";
         | 
| @@ -27,15 +27,19 @@ | |
| 27 | 
             
            </script>
         | 
| 28 |  | 
| 29 | 
             
            <script lang="ts">
         | 
| 30 | 
            -
            	 | 
| 31 |  | 
| 32 | 
            -
            	let  | 
| 33 |  | 
| 34 | 
            -
            	 | 
| 35 | 
            -
             | 
| 36 | 
            -
            	 | 
| 37 | 
            -
            		 | 
| 38 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
| 39 |  | 
| 40 | 
             
            	function onSubmit(e: Event) {
         | 
| 41 | 
             
            		e.preventDefault();
         | 
| @@ -43,11 +47,11 @@ | |
| 43 | 
             
            	}
         | 
| 44 | 
             
            </script>
         | 
| 45 |  | 
| 46 | 
            -
            <dialog bind:this={dialog}  | 
| 47 | 
             
            	{#if current}
         | 
| 48 | 
             
            		<div class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/85">
         | 
| 49 | 
             
            			<form
         | 
| 50 | 
            -
            				 | 
| 51 | 
             
            				class="relative w-xl rounded-lg bg-white shadow-sm dark:bg-gray-900"
         | 
| 52 | 
             
            				use:clickOutside={resolvePrompt}
         | 
| 53 | 
             
            			>
         | 
| @@ -58,7 +62,7 @@ | |
| 58 | 
             
            					<button
         | 
| 59 | 
             
            						type="button"
         | 
| 60 | 
             
            						class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
         | 
| 61 | 
            -
            						 | 
| 62 | 
             
            					>
         | 
| 63 | 
             
            						<div class="text-xl">
         | 
| 64 | 
             
            							<IconCross />
         | 
| @@ -70,7 +74,7 @@ | |
| 70 | 
             
            				<div class="p-4 md:p-5">
         | 
| 71 | 
             
            					<label class="flex flex-col gap-2 font-medium text-gray-900 dark:text-white">
         | 
| 72 | 
             
            						<!-- This is fine in dialogs -->
         | 
| 73 | 
            -
            						<!-- svelte-ignore  | 
| 74 | 
             
            						<input
         | 
| 75 | 
             
            							bind:value={current.value}
         | 
| 76 | 
             
            							placeholder={current.placeholder}
         | 
|  | |
| 1 | 
            +
            <script lang="ts" module>
         | 
| 2 | 
             
            	import { clickOutside } from "$lib/actions/click-outside.js";
         | 
| 3 | 
             
            	import { writable } from "svelte/store";
         | 
| 4 | 
             
            	import IconCross from "~icons/carbon/close";
         | 
|  | |
| 27 | 
             
            </script>
         | 
| 28 |  | 
| 29 | 
             
            <script lang="ts">
         | 
| 30 | 
            +
            	import { run } from 'svelte/legacy';
         | 
| 31 |  | 
| 32 | 
            +
            	let current = $derived($prompts?.[0]);
         | 
| 33 |  | 
| 34 | 
            +
            	let dialog: HTMLDialogElement | undefined = $state();
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            	run(() => {
         | 
| 37 | 
            +
            		if (current) {
         | 
| 38 | 
            +
            			dialog?.showModal();
         | 
| 39 | 
            +
            		} else {
         | 
| 40 | 
            +
            			dialog?.close();
         | 
| 41 | 
            +
            		}
         | 
| 42 | 
            +
            	});
         | 
| 43 |  | 
| 44 | 
             
            	function onSubmit(e: Event) {
         | 
| 45 | 
             
            		e.preventDefault();
         | 
|  | |
| 47 | 
             
            	}
         | 
| 48 | 
             
            </script>
         | 
| 49 |  | 
| 50 | 
            +
            <dialog bind:this={dialog} onclose={resolvePrompt}>
         | 
| 51 | 
             
            	{#if current}
         | 
| 52 | 
             
            		<div class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/85">
         | 
| 53 | 
             
            			<form
         | 
| 54 | 
            +
            				onsubmit={onSubmit}
         | 
| 55 | 
             
            				class="relative w-xl rounded-lg bg-white shadow-sm dark:bg-gray-900"
         | 
| 56 | 
             
            				use:clickOutside={resolvePrompt}
         | 
| 57 | 
             
            			>
         | 
|  | |
| 62 | 
             
            					<button
         | 
| 63 | 
             
            						type="button"
         | 
| 64 | 
             
            						class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
         | 
| 65 | 
            +
            						onclick={resolvePrompt}
         | 
| 66 | 
             
            					>
         | 
| 67 | 
             
            						<div class="text-xl">
         | 
| 68 | 
             
            							<IconCross />
         | 
|  | |
| 74 | 
             
            				<div class="p-4 md:p-5">
         | 
| 75 | 
             
            					<label class="flex flex-col gap-2 font-medium text-gray-900 dark:text-white">
         | 
| 76 | 
             
            						<!-- This is fine in dialogs -->
         | 
| 77 | 
            +
            						<!-- svelte-ignore a11y_autofocus -->
         | 
| 78 | 
             
            						<input
         | 
| 79 | 
             
            							bind:value={current.value}
         | 
| 80 | 
             
            							placeholder={current.placeholder}
         | 
    	
        src/routes/+layout.svelte
    CHANGED
    
    | @@ -3,9 +3,14 @@ | |
| 3 | 
             
            	import DebugMenu from "$lib/components/debug-menu.svelte";
         | 
| 4 | 
             
            	import Prompts from "$lib/components/prompts.svelte";
         | 
| 5 | 
             
            	import Toaster from "$lib/components/toaster.svelte";
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 6 | 
             
            </script>
         | 
| 7 |  | 
| 8 | 
            -
             | 
| 9 | 
             
            <DebugMenu />
         | 
| 10 | 
             
            <Prompts />
         | 
| 11 | 
             
            <Toaster />
         | 
|  | |
| 3 | 
             
            	import DebugMenu from "$lib/components/debug-menu.svelte";
         | 
| 4 | 
             
            	import Prompts from "$lib/components/prompts.svelte";
         | 
| 5 | 
             
            	import Toaster from "$lib/components/toaster.svelte";
         | 
| 6 | 
            +
            	interface Props {
         | 
| 7 | 
            +
            		children?: import('svelte').Snippet;
         | 
| 8 | 
            +
            	}
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            	let { children }: Props = $props();
         | 
| 11 | 
             
            </script>
         | 
| 12 |  | 
| 13 | 
            +
            {@render children?.()}
         | 
| 14 | 
             
            <DebugMenu />
         | 
| 15 | 
             
            <Prompts />
         | 
| 16 | 
             
            <Toaster />
         | 
