Some UI tweaks (#93)
Browse files* **Styling:**
* The bottom action bar in
[`src/lib/components/inference-playground/playground.svelte`](src/lib/components/inference-playground/playground.svelte:122)
and its buttons were made more compact.
* **User Experience:**
* A welcome message is now displayed for new conversations in
[`src/lib/components/inference-playground/conversation.svelte`](src/lib/components/inference-playground/conversation.svelte:59).
* The validation preventing consecutive user messages was removed in
[`src/lib/components/inference-playground/message-textarea.svelte`](src/lib/components/inference-playground/message-textarea.svelte:24),
allowing for a more natural conversation flow.
* **State Management:**
* In
[`src/lib/state/conversations.svelte.ts`](src/lib/state/conversations.svelte.ts:81),
new conversations are now initialized with an empty message list, and so
when you send the first message it's not spawning a 2nd user message.
@@ -64,19 +64,19 @@
|
|
64 |
}
|
65 |
|
66 |
@utility btn {
|
67 |
-
@apply flex h-[39px] items-center justify-center gap-2 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:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-
|
68 |
}
|
69 |
|
70 |
@utility btn-sm {
|
71 |
-
@apply flex h-[32px] items-center justify-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 py-2 text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-
|
72 |
}
|
73 |
|
74 |
@utility btn-xs {
|
75 |
-
@apply flex h-[28px] items-center justify-center gap-1 rounded border border-gray-200 bg-white px-2 py-1.5 text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-
|
76 |
}
|
77 |
|
78 |
@utility btn-mini {
|
79 |
-
@apply flex h-[24px] items-center justify-center gap-0.5 rounded-sm border border-gray-200 bg-white px-1.5 py-1 text-[10px] font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-2 focus:ring-gray-100 focus:outline-hidden dark:border-gray-
|
80 |
}
|
81 |
|
82 |
@utility custom-outline {
|
|
|
64 |
}
|
65 |
|
66 |
@utility btn {
|
67 |
+
@apply flex h-[39px] items-center justify-center gap-2 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:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white dark:focus:ring-gray-700;
|
68 |
}
|
69 |
|
70 |
@utility btn-sm {
|
71 |
+
@apply flex h-[32px] items-center justify-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 py-2 text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white dark:focus:ring-gray-700;
|
72 |
}
|
73 |
|
74 |
@utility btn-xs {
|
75 |
+
@apply flex h-[28px] items-center justify-center gap-1 rounded border border-gray-200 bg-white px-2 py-1.5 text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white dark:focus:ring-gray-700;
|
76 |
}
|
77 |
|
78 |
@utility btn-mini {
|
79 |
+
@apply flex h-[24px] items-center justify-center gap-0.5 rounded-sm border border-gray-200 bg-white px-1.5 py-1 text-[10px] font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-2 focus:ring-gray-100 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white dark:focus:ring-gray-700;
|
80 |
}
|
81 |
|
82 |
@utility custom-outline {
|
@@ -57,15 +57,22 @@
|
|
57 |
bind:this={messageContainer}
|
58 |
>
|
59 |
{#if !viewCode}
|
60 |
-
{#
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
{:else}
|
70 |
<CodeSnippets {conversation} {onCloseCode} />
|
71 |
{/if}
|
|
|
57 |
bind:this={messageContainer}
|
58 |
>
|
59 |
{#if !viewCode}
|
60 |
+
{#if conversation.data.messages?.length}
|
61 |
+
{#each conversation.data.messages as message, index}
|
62 |
+
<Message
|
63 |
+
{message}
|
64 |
+
{index}
|
65 |
+
{conversation}
|
66 |
+
onDelete={() => conversation.deleteMessage(index)}
|
67 |
+
onRegen={() => regenMessage(index)}
|
68 |
+
/>
|
69 |
+
{:else}
|
70 |
+
<div class="m-auto flex flex-col items-center gap-2 text-center px-4 text-balance">
|
71 |
+
<h1 class="text-2xl font-semibold">Welcome to Hugging Face Inference Playground</h1>
|
72 |
+
<p class="text-lg text-gray-500">Try hundreds of models on different providers</p>
|
73 |
+
</div>
|
74 |
+
{/each}
|
75 |
+
{/if}
|
76 |
{:else}
|
77 |
<CodeSnippets {conversation} {onCloseCode} />
|
78 |
{/if}
|
@@ -21,20 +21,9 @@
|
|
21 |
|
22 |
async function sendMessage() {
|
23 |
const c = conversations.active;
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
addToast({
|
28 |
-
title: "Cannot add message",
|
29 |
-
description: "Cannot have multiple user messages in a row",
|
30 |
-
|
31 |
-
variant: "error",
|
32 |
-
});
|
33 |
-
} else {
|
34 |
-
await Promise.all(c.map(c => c.addMessage({ role: "user", content: input })));
|
35 |
-
c.forEach(c => c.genNextMessage());
|
36 |
-
input = "";
|
37 |
-
}
|
38 |
}
|
39 |
|
40 |
const autosized = new TextareaAutosize();
|
@@ -42,9 +31,9 @@
|
|
42 |
|
43 |
<svelte:window onkeydown={onKeydown} />
|
44 |
|
45 |
-
<div class="mt-auto
|
46 |
<label
|
47 |
-
class="flex w-full items-end rounded-[32px] bg-gray-200 p-2 pl-
|
48 |
>
|
49 |
<textarea
|
50 |
placeholder="Enter your message"
|
|
|
21 |
|
22 |
async function sendMessage() {
|
23 |
const c = conversations.active;
|
24 |
+
await Promise.all(c.map(c => c.addMessage({ role: "user", content: input })));
|
25 |
+
c.forEach(c => c.genNextMessage());
|
26 |
+
input = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
}
|
28 |
|
29 |
const autosized = new TextareaAutosize();
|
|
|
31 |
|
32 |
<svelte:window onkeydown={onKeydown} />
|
33 |
|
34 |
+
<div class="mt-auto px-2">
|
35 |
<label
|
36 |
+
class="flex w-full items-end rounded-[32px] bg-gray-200 p-2 pl-6 outline-gray-500 focus-within:outline-2 dark:bg-gray-800"
|
37 |
>
|
38 |
<textarea
|
39 |
placeholder="Enter your message"
|
@@ -119,18 +119,16 @@
|
|
119 |
|
120 |
<!-- Bottom bar -->
|
121 |
<div
|
122 |
-
class="relative mt-auto flex h-
|
123 |
>
|
124 |
<div class="flex flex-1 justify-start gap-x-2">
|
125 |
{#if !compareActive}
|
126 |
<button
|
127 |
type="button"
|
128 |
onclick={() => (viewSettings = !viewSettings)}
|
129 |
-
class="flex h-[
|
130 |
>
|
131 |
-
<
|
132 |
-
<IconSettings />
|
133 |
-
</div>
|
134 |
{!viewSettings ? "Settings" : "Hide"}
|
135 |
</button>
|
136 |
{/if}
|
@@ -139,7 +137,7 @@
|
|
139 |
<button
|
140 |
type="button"
|
141 |
onclick={conversations.reset}
|
142 |
-
class="btn size-[
|
143 |
{...tooltip.trigger}
|
144 |
data-test-id={TEST_IDS.reset}
|
145 |
>
|
@@ -170,7 +168,7 @@
|
|
170 |
<button
|
171 |
type="button"
|
172 |
onclick={() => (viewCode = !viewCode)}
|
173 |
-
class="btn"
|
174 |
{@attach observe({ name: ObservedElements.BottomActions, useRaf: true })}
|
175 |
>
|
176 |
<IconCode />
|
|
|
119 |
|
120 |
<!-- Bottom bar -->
|
121 |
<div
|
122 |
+
class="relative mt-auto flex h-14 shrink-0 items-center justify-center gap-2 overflow-hidden px-3 whitespace-nowrap"
|
123 |
>
|
124 |
<div class="flex flex-1 justify-start gap-x-2">
|
125 |
{#if !compareActive}
|
126 |
<button
|
127 |
type="button"
|
128 |
onclick={() => (viewSettings = !viewSettings)}
|
129 |
+
class="flex h-[28px]! px-2 items-center gap-1 rounded-lg border border-gray-200 bg-white 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-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
130 |
>
|
131 |
+
<IconSettings />
|
|
|
|
|
132 |
{!viewSettings ? "Settings" : "Hide"}
|
133 |
</button>
|
134 |
{/if}
|
|
|
137 |
<button
|
138 |
type="button"
|
139 |
onclick={conversations.reset}
|
140 |
+
class="btn size-[28px]! p-0!"
|
141 |
{...tooltip.trigger}
|
142 |
data-test-id={TEST_IDS.reset}
|
143 |
>
|
|
|
168 |
<button
|
169 |
type="button"
|
170 |
onclick={() => (viewCode = !viewCode)}
|
171 |
+
class="btn h-[28px]! px-2!"
|
172 |
{@attach observe({ name: ObservedElements.BottomActions, useRaf: true })}
|
173 |
>
|
174 |
<IconCode />
|
@@ -57,8 +57,6 @@ export type ConversationEntityMembers = MembersOnly<ConversationEntity>;
|
|
57 |
|
58 |
const conversationsRepo = repo(ConversationEntity, idb);
|
59 |
|
60 |
-
const startMessageUser: ConversationMessage = { role: "user", content: "" };
|
61 |
-
|
62 |
export const emptyModel: Model = {
|
63 |
_id: "",
|
64 |
inferenceProviderMapping: [],
|
@@ -78,7 +76,7 @@ function getDefaultConversation(projectId: string) {
|
|
78 |
projectId,
|
79 |
modelId: models.trending[0]?.id ?? models.remote[0]?.id ?? emptyModel.id,
|
80 |
config: { ...defaultGenerationConfig },
|
81 |
-
messages: [
|
82 |
streaming: true,
|
83 |
createdAt: new Date(),
|
84 |
} satisfies Partial<ConversationEntityMembers>;
|
|
|
57 |
|
58 |
const conversationsRepo = repo(ConversationEntity, idb);
|
59 |
|
|
|
|
|
60 |
export const emptyModel: Model = {
|
61 |
_id: "",
|
62 |
inferenceProviderMapping: [],
|
|
|
76 |
projectId,
|
77 |
modelId: models.trending[0]?.id ?? models.remote[0]?.id ?? emptyModel.id,
|
78 |
config: { ...defaultGenerationConfig },
|
79 |
+
messages: [],
|
80 |
streaming: true,
|
81 |
createdAt: new Date(),
|
82 |
} satisfies Partial<ConversationEntityMembers>;
|