Spaces:
Runtime error
Runtime error
File size: 6,281 Bytes
0b5f169 7357f85 4b8b411 39318e7 4b8b411 7357f85 0a59c60 e63e7c7 936176c 1754cbb 936176c d37b3c2 936176c da50250 936176c 39318e7 003aab5 0a59c60 d37b3c2 d4fcb0f 65131f6 0bcf467 d37b3c2 60216ec 65131f6 c6262f2 90c9ef4 65131f6 de14ada 65131f6 d37b3c2 936176c d37b3c2 60216ec d37b3c2 0b5f169 936176c d37b3c2 936176c d37b3c2 936176c d37b3c2 73b6f4f f250f57 64cfbce 936176c 0b5f169 8c5a2cf 7965df6 39318e7 0b5f169 73b6f4f 24e0413 0b5f169 24e0413 936176c 24e0413 d47c403 64cfbce 24e0413 0b5f169 24e0413 0b5f169 24e0413 936176c 24e0413 7b1d26a 24e0413 0b5f169 24e0413 0b5f169 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
<script lang="ts">
import type { Conversation } from "$lib/types.js";
import { createEventDispatcher, onMount, tick } from "svelte";
import { models } from "$lib/stores/models.js";
import { getTrending } from "$lib/utils/model.js";
import fuzzysearch from "$lib/utils/search.js";
import IconSearch from "~icons/carbon/search";
import IconStar from "~icons/carbon/star";
interface Props {
conversation: Conversation;
}
let { conversation }: Props = $props();
let backdropEl = $state<HTMLDivElement>();
let highlightIdx = $state(0);
let ignoreCursorHighlight = $state(false);
let containerEl = $state<HTMLDivElement>();
let query = $state("");
const dispatch = createEventDispatcher<{ modelSelected: string; close: void }>();
let trendingModels = $derived(getTrending($models));
let featuredModels = $derived(fuzzysearch({ needle: query, haystack: trendingModels, property: "id" }));
let otherModels = $derived(fuzzysearch({ needle: query, haystack: $models, property: "id" }));
onMount(() => {
if (featuredModels.findIndex(model => model.id === conversation.model.id) !== -1) {
highlightIdx = featuredModels.findIndex(model => model.id === conversation.model.id);
} else {
highlightIdx = featuredModels.length + otherModels.findIndex(model => model.id === conversation.model.id);
}
});
type ScrollLogicalPosition = "center" | "end" | "nearest" | "start";
function handleKeydown(event: KeyboardEvent) {
const { key } = event;
let scrollLogicalPosition: ScrollLogicalPosition = "end";
if (key === "Escape") {
event.preventDefault();
dispatch("close");
} else if (key === "Enter") {
event.preventDefault();
const highlightedEl = document.querySelector(".highlighted");
if (highlightedEl) {
(highlightedEl as HTMLButtonElement).click();
}
} else if (key === "ArrowUp") {
event.preventDefault();
highlightIdx--;
scrollLogicalPosition = "start";
ignoreCursorHighlight = true;
} else if (key === "ArrowDown") {
event.preventDefault();
highlightIdx++;
ignoreCursorHighlight = true;
}
const n = featuredModels.length + otherModels.length;
highlightIdx = ((highlightIdx % n) + n) % n;
scrollToResult(scrollLogicalPosition);
}
async function scrollToResult(block: ScrollLogicalPosition) {
await tick();
const highlightedEl = document.querySelector(".highlighted");
if (containerEl && highlightedEl) {
const { bottom: containerBottom, top: containerTop } = containerEl.getBoundingClientRect();
const { bottom: highlightedBottom, top: highlightedTop } = highlightedEl.getBoundingClientRect();
if (highlightedBottom > containerBottom || containerTop > highlightedTop) {
highlightedEl.scrollIntoView({ block });
}
}
}
function highlightRow(idx: number) {
if (!ignoreCursorHighlight) {
highlightIdx = idx;
}
}
function handleBackdropClick(event: MouseEvent) {
event.stopPropagation();
if (window?.getSelection()?.toString()) {
return;
}
if (event.target === backdropEl) {
dispatch("close");
}
}
</script>
<svelte:window onkeydown={handleKeydown} onmousemove={() => (ignoreCursorHighlight = false)} />
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
class="fixed inset-0 z-10 flex h-screen items-start justify-center bg-black/85 pt-32"
bind:this={backdropEl}
onclick={handleBackdropClick}
>
<div class="flex w-full max-w-[600px] items-start justify-center overflow-hidden p-10 text-left whitespace-nowrap">
<div
class="flex h-full w-full flex-col overflow-hidden rounded-lg border bg-white text-gray-900 shadow-md dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300"
bind:this={containerEl}
>
<div class="flex items-center border-b px-3 dark:border-gray-800">
<div class="mr-2 text-sm">
<IconSearch />
</div>
<!-- svelte-ignore a11y_autofocus -->
<input
autofocus
class="flex h-10 w-full rounded-md bg-transparent py-3 text-sm placeholder-gray-400 outline-hidden"
placeholder="Search models ..."
bind:value={query}
/>
</div>
<div class="max-h-[300px] overflow-x-hidden overflow-y-auto">
{#if featuredModels.length}
<div>
<div class="px-2 py-1.5 text-xs font-medium text-gray-500">Trending</div>
<div>
{#each featuredModels as model, idx}
{@const [nameSpace, modelName] = model.id.split("/")}
<button
class="flex w-full cursor-pointer items-center px-2 py-1.5 text-sm {highlightIdx === idx
? 'highlighted bg-gray-100 dark:bg-gray-800'
: ''}"
onmouseenter={() => highlightRow(idx)}
onclick={() => {
dispatch("modelSelected", model.id);
dispatch("close");
}}
>
<div class="lucide lucide-star mr-1.5 size-4 text-yellow-400">
<IconStar />
</div>
<span class="inline-flex items-center"
><span class="text-gray-500 dark:text-gray-400">{nameSpace}</span><span
class="mx-1 text-gray-300 dark:text-gray-700">/</span
><span class="text-black dark:text-white">{modelName}</span></span
>
</button>
{/each}
</div>
</div>
{/if}
{#if otherModels.length}
<div>
<div class="px-2 py-1.5 text-xs font-medium text-gray-500">Other Models</div>
<div>
{#each otherModels as model, _idx}
{@const [nameSpace, modelName] = model.id.split("/")}
{@const idx = featuredModels.length + _idx}
<button
class="flex w-full cursor-pointer items-center px-2 py-1.5 text-sm {highlightIdx === idx
? 'highlighted bg-gray-100 dark:bg-gray-800'
: ''}"
onmouseenter={() => highlightRow(idx)}
onclick={() => {
dispatch("modelSelected", model.id);
dispatch("close");
}}
>
<span class="inline-flex items-center"
><span class="text-gray-500 dark:text-gray-400">{nameSpace}</span><span
class="mx-1 text-gray-300 dark:text-gray-700">/</span
><span class="text-black dark:text-white">{modelName}</span></span
>
</button>
{/each}
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
|