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 type { Conversation } from "$lib/types.js";
3
 
4
- import { tick } from "svelte";
5
 
6
  import IconPlus from "~icons/carbon/add";
7
  import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
8
- import Message from "./InferencePlaygroundMessage.svelte";
9
 
10
- export let conversation: Conversation;
11
- export let loading: boolean;
12
- export let viewCode: boolean;
13
- export let compareActive: boolean;
 
 
14
 
15
- let shouldScrollToBottom = true;
16
- let isProgrammaticScroll = true;
17
- let conversationLength = conversation.messages.length;
18
 
19
- let messageContainer: HTMLDivElement | null = null;
 
 
20
 
21
- async function resizeMessageTextAreas() {
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
- $: viewCode, resizeMessageTextAreas();
 
 
 
 
 
 
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
- on:scroll={() => {
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 message, messageIdx}
95
- <Message
96
- class="border-b"
97
- bind:message
98
- {loading}
99
- on:input={resizeMessageTextAreas}
100
- on:delete={() => deleteMessage(messageIdx)}
101
- autofocus={!loading && messageIdx === conversation.messages.length - 1}
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
- on:click={addMessage}
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>