Thomas G. Lopes
commited on
Commit
·
5851d63
1
Parent(s):
e48cc8b
conversation to runes
Browse files
eslint.config.mjs
CHANGED
@@ -67,7 +67,7 @@ export default [
|
|
67 |
rules: {
|
68 |
"require-yield": "off",
|
69 |
"@typescript-eslint/no-explicit-any": "error",
|
70 |
-
"@typescript-eslint/no-non-null-assertion": "error",
|
71 |
|
72 |
"@typescript-eslint/no-unused-vars": [
|
73 |
"error",
|
|
|
67 |
rules: {
|
68 |
"require-yield": "off",
|
69 |
"@typescript-eslint/no-explicit-any": "error",
|
70 |
+
// "@typescript-eslint/no-non-null-assertion": "error",
|
71 |
|
72 |
"@typescript-eslint/no-unused-vars": [
|
73 |
"error",
|
src/lib/components/InferencePlayground/InferencePlaygroundConversation.svelte
CHANGED
@@ -1,36 +1,25 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import
|
3 |
|
4 |
-
import {
|
5 |
|
6 |
import IconPlus from "~icons/carbon/add";
|
7 |
import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
|
8 |
-
import Message from "./InferencePlaygroundMessage.svelte";
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
14 |
|
15 |
-
let
|
16 |
-
let isProgrammaticScroll = true;
|
17 |
-
let conversationLength = conversation.messages.length;
|
18 |
|
19 |
-
let
|
|
|
|
|
20 |
|
21 |
-
|
22 |
-
// ideally we would use CSS "field-sizing:content". However, it is currently only supported on Chrome.
|
23 |
-
await tick();
|
24 |
-
if (messageContainer) {
|
25 |
-
const containerScrollTop = messageContainer.scrollTop;
|
26 |
-
const textareaEls = messageContainer.querySelectorAll("textarea");
|
27 |
-
for (const textarea of textareaEls) {
|
28 |
-
textarea.style.height = "0px";
|
29 |
-
textarea.style.height = textarea.scrollHeight + "px";
|
30 |
-
}
|
31 |
-
messageContainer.scrollTop = containerScrollTop;
|
32 |
-
}
|
33 |
-
}
|
34 |
|
35 |
function scrollToBottom() {
|
36 |
if (messageContainer) {
|
@@ -39,22 +28,21 @@
|
|
39 |
}
|
40 |
}
|
41 |
|
42 |
-
|
43 |
if (conversation.messages.at(-1)) {
|
44 |
-
resizeMessageTextAreas();
|
45 |
if (shouldScrollToBottom) {
|
46 |
scrollToBottom();
|
47 |
}
|
48 |
}
|
49 |
-
}
|
50 |
-
|
51 |
-
$: if (conversation.messages.length !== conversationLength) {
|
52 |
-
// enable automatic scrolling when new message was added
|
53 |
-
conversationLength = conversation.messages.length;
|
54 |
-
shouldScrollToBottom = true;
|
55 |
-
}
|
56 |
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
function addMessage() {
|
60 |
const msgs = conversation.messages.slice();
|
@@ -74,15 +62,13 @@
|
|
74 |
}
|
75 |
</script>
|
76 |
|
77 |
-
<svelte:window on:resize={resizeMessageTextAreas} />
|
78 |
-
|
79 |
<div
|
80 |
class="@container flex flex-col overflow-x-hidden overflow-y-auto {compareActive
|
81 |
? 'max-h-[calc(100dvh-5.8rem-2.5rem-75px)] md:max-h-[calc(100dvh-5.8rem-2.5rem)]'
|
82 |
: 'max-h-[calc(100dvh-5.8rem-2.5rem-75px)] md:max-h-[calc(100dvh-5.8rem)]'}"
|
83 |
class:animate-pulse={loading && !conversation.streaming}
|
84 |
bind:this={messageContainer}
|
85 |
-
|
86 |
// disable automatic scrolling is user initiates scroll
|
87 |
if (!isProgrammaticScroll) {
|
88 |
shouldScrollToBottom = false;
|
@@ -91,20 +77,37 @@
|
|
91 |
}}
|
92 |
>
|
93 |
{#if !viewCode}
|
94 |
-
{#each conversation.messages as
|
95 |
-
<
|
96 |
-
class="border-b"
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
{/each}
|
104 |
|
105 |
<button
|
106 |
class="flex px-3.5 py-6 hover:bg-gray-50 md:px-6 dark:hover:bg-gray-800/50"
|
107 |
-
|
108 |
disabled={loading}
|
109 |
>
|
110 |
<div class="flex items-center gap-2 p-0! text-sm font-semibold">
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { run } from "svelte/legacy";
|
3 |
|
4 |
+
import type { Conversation } from "$lib/types.js";
|
5 |
|
6 |
import IconPlus from "~icons/carbon/add";
|
7 |
import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
|
|
|
8 |
|
9 |
+
interface Props {
|
10 |
+
conversation: Conversation;
|
11 |
+
loading: boolean;
|
12 |
+
viewCode: boolean;
|
13 |
+
compareActive: boolean;
|
14 |
+
}
|
15 |
|
16 |
+
let { conversation = $bindable(), loading, viewCode, compareActive }: Props = $props();
|
|
|
|
|
17 |
|
18 |
+
let shouldScrollToBottom = $state(true);
|
19 |
+
let isProgrammaticScroll = $state(true);
|
20 |
+
let conversationLength = $state(conversation.messages.length);
|
21 |
|
22 |
+
let messageContainer: HTMLDivElement | null = $state(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
function scrollToBottom() {
|
25 |
if (messageContainer) {
|
|
|
28 |
}
|
29 |
}
|
30 |
|
31 |
+
run(() => {
|
32 |
if (conversation.messages.at(-1)) {
|
|
|
33 |
if (shouldScrollToBottom) {
|
34 |
scrollToBottom();
|
35 |
}
|
36 |
}
|
37 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
+
run(() => {
|
40 |
+
if (conversation.messages.length !== conversationLength) {
|
41 |
+
// enable automatic scrolling when new message was added
|
42 |
+
conversationLength = conversation.messages.length;
|
43 |
+
shouldScrollToBottom = true;
|
44 |
+
}
|
45 |
+
});
|
46 |
|
47 |
function addMessage() {
|
48 |
const msgs = conversation.messages.slice();
|
|
|
62 |
}
|
63 |
</script>
|
64 |
|
|
|
|
|
65 |
<div
|
66 |
class="@container flex flex-col overflow-x-hidden overflow-y-auto {compareActive
|
67 |
? 'max-h-[calc(100dvh-5.8rem-2.5rem-75px)] md:max-h-[calc(100dvh-5.8rem-2.5rem)]'
|
68 |
: 'max-h-[calc(100dvh-5.8rem-2.5rem-75px)] md:max-h-[calc(100dvh-5.8rem)]'}"
|
69 |
class:animate-pulse={loading && !conversation.streaming}
|
70 |
bind:this={messageContainer}
|
71 |
+
onscroll={() => {
|
72 |
// disable automatic scrolling is user initiates scroll
|
73 |
if (!isProgrammaticScroll) {
|
74 |
shouldScrollToBottom = false;
|
|
|
77 |
}}
|
78 |
>
|
79 |
{#if !viewCode}
|
80 |
+
{#each conversation.messages as msg, idx}
|
81 |
+
<div
|
82 |
+
class=" group/message group grid grid-cols-[1fr_2.5rem] items-start gap-2 border-b px-3.5 pt-4 pb-6 hover:bg-gray-100/70 @-2xl:grid-cols-[130px_1fr_2.5rem] @2xl:grid-rows-1 @2xl:gap-4 @2xl:px-6 dark:border-gray-800 dark:hover:bg-gray-800/30"
|
83 |
+
class:pointer-events-none={loading}
|
84 |
+
>
|
85 |
+
<div class="col-span-2 pt-3 pb-1 text-sm font-semibold uppercase @2xl:col-span-1 @2xl:pb-2">
|
86 |
+
{msg.role}
|
87 |
+
</div>
|
88 |
+
<!-- svelte-ignore a11y_autofocus -->
|
89 |
+
<!-- svelte-ignore a11y_positive_tabindex -->
|
90 |
+
<textarea
|
91 |
+
autofocus={idx === conversation.messages.length - 1}
|
92 |
+
bind:value={conversation.messages[idx]!.content}
|
93 |
+
placeholder="Enter {msg.role} message"
|
94 |
+
class="resize-none overflow-hidden rounded-sm bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:resize-y hover:bg-white focus:resize-y focus:bg-white focus:ring-3 @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
95 |
+
rows="1"
|
96 |
+
tabindex="2"
|
97 |
+
></textarea>
|
98 |
+
<button
|
99 |
+
tabindex="0"
|
100 |
+
onclick={() => deleteMessage(idx)}
|
101 |
+
type="button"
|
102 |
+
class="mt-1.5 size-8 rounded-lg border border-gray-200 bg-white text-xs font-medium text-gray-900 group-hover/message:block hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden sm: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"
|
103 |
+
>✕</button
|
104 |
+
>
|
105 |
+
</div>
|
106 |
{/each}
|
107 |
|
108 |
<button
|
109 |
class="flex px-3.5 py-6 hover:bg-gray-50 md:px-6 dark:hover:bg-gray-800/50"
|
110 |
+
onclick={addMessage}
|
111 |
disabled={loading}
|
112 |
>
|
113 |
<div class="flex items-center gap-2 p-0! text-sm font-semibold">
|
src/lib/components/InferencePlayground/InferencePlaygroundMessage.svelte
DELETED
@@ -1,41 +0,0 @@
|
|
1 |
-
<script lang="ts">
|
2 |
-
import { createEventDispatcher } from "svelte";
|
3 |
-
import type { ConversationMessage } from "$lib/types.js";
|
4 |
-
|
5 |
-
export let message: ConversationMessage;
|
6 |
-
export let loading: boolean = false;
|
7 |
-
export let autofocus: boolean = false;
|
8 |
-
|
9 |
-
const dispatch = createEventDispatcher<{ delete: void; input: void }>();
|
10 |
-
</script>
|
11 |
-
|
12 |
-
<div
|
13 |
-
class="group/message group grid grid-cols-[1fr_2.5rem] items-start gap-2 px-3.5 pt-4 pb-6 hover:bg-gray-100/70 @-2xl:grid-cols-[130px_1fr_2.5rem] @2xl:grid-rows-1 @2xl:gap-4 @2xl:px-6 dark:border-gray-800 dark:hover:bg-gray-800/30 {$$props.class}"
|
14 |
-
class:pointer-events-none={loading}
|
15 |
-
>
|
16 |
-
<div class="col-span-2 pt-3 pb-1 text-sm font-semibold uppercase @2xl:col-span-1 @2xl:pb-2">
|
17 |
-
{message.role}
|
18 |
-
</div>
|
19 |
-
<!-- svelte-ignore a11y-autofocus -->
|
20 |
-
<!-- svelte-ignore a11y-positive-tabindex -->
|
21 |
-
<textarea
|
22 |
-
{autofocus}
|
23 |
-
bind:value={message.content}
|
24 |
-
placeholder="Enter {message.role} message"
|
25 |
-
class="resize-none overflow-hidden rounded-sm bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:resize-y hover:bg-white focus:resize-y focus:bg-white focus:ring-3 @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
26 |
-
rows="1"
|
27 |
-
tabindex="2"
|
28 |
-
on:input={() => {
|
29 |
-
dispatch("input");
|
30 |
-
}}
|
31 |
-
></textarea>
|
32 |
-
<button
|
33 |
-
tabindex="0"
|
34 |
-
on:click={() => {
|
35 |
-
dispatch("delete");
|
36 |
-
}}
|
37 |
-
type="button"
|
38 |
-
class="mt-1.5 size-8 rounded-lg border border-gray-200 bg-white text-xs font-medium text-gray-900 group-hover/message:block hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden sm: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"
|
39 |
-
>✕</button
|
40 |
-
>
|
41 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|