Thomas G. Lopes commited on
Commit
55549a7
·
1 Parent(s): c2603a4

improve layout

Browse files
src/lib/components/inference-playground/model-selector.svelte CHANGED
@@ -41,16 +41,18 @@
41
  <button
42
  {id}
43
  class={cn(
44
- "focus-outline relative flex items-center gap-2 overflow-hidden rounded-lg border",
45
  "bg-gray-100/80 px-3 py-1.5 text-sm leading-tight whitespace-nowrap shadow-sm",
46
  "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
47
  )}
48
  onclick={() => (showModelPickerModal = true)}
49
  >
50
- <Avatar model={conversation.model} orgName={nameSpace} size="sm" />
51
- <div class="overflow-hidden">
52
- <span class="text-xs text-gray-500 dark:text-gray-400">{nameSpace}/</span>
53
- <span class="truncate font-medium">{modelName}</span>
 
 
54
  </div>
55
  <IconCaret class="size-4 flex-none text-gray-500" />
56
  </button>
 
41
  <button
42
  {id}
43
  class={cn(
44
+ "focus-outline relative flex w-full items-center justify-between overflow-hidden rounded-lg border",
45
  "bg-gray-100/80 px-3 py-1.5 text-sm leading-tight whitespace-nowrap shadow-sm",
46
  "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
47
  )}
48
  onclick={() => (showModelPickerModal = true)}
49
  >
50
+ <div class="flex items-center gap-2 overflow-hidden">
51
+ <Avatar model={conversation.model} orgName={nameSpace} size="sm" />
52
+ <div class="overflow-hidden">
53
+ <span class="text-xs text-gray-500 dark:text-gray-400">{nameSpace}/</span>
54
+ <span class="truncate font-medium">{modelName}</span>
55
+ </div>
56
  </div>
57
  <IconCaret class="size-4 flex-none text-gray-500" />
58
  </button>
src/lib/components/inference-playground/playground.svelte CHANGED
@@ -118,8 +118,8 @@
118
  <!-- Left side: Model selector, provider selector, and compare -->
119
  <div class="flex items-center gap-3">
120
  {#if !compareActive && conversations.active[0]}
121
- <!-- Model and provider stacked vertically -->
122
- <div class="flex flex-col gap-1">
123
  <ModelSelector conversation={conversations.active[0]} compact />
124
  {#if isHFModel(conversations.active[0].model)}
125
  <!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
 
118
  <!-- Left side: Model selector, provider selector, and compare -->
119
  <div class="flex items-center gap-3">
120
  {#if !compareActive && conversations.active[0]}
121
+ <!-- Model and provider stacked vertically with fixed width -->
122
+ <div class="flex w-72 flex-col gap-1">
123
  <ModelSelector conversation={conversations.active[0]} compact />
124
  {#if isHFModel(conversations.active[0].model)}
125
  <!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
src/lib/components/inference-playground/provider-select.svelte CHANGED
@@ -21,7 +21,6 @@
21
  function reset(providers: typeof conversation.model.inferenceProviderMapping) {
22
  const validProvider = providers.find(p => p.provider === conversation.data.provider);
23
  if (validProvider || conversation.data.provider === "auto") return;
24
- // Default to auto provider if no valid provider is set
25
  conversation.update({ provider: "auto", autoPolicy: "default" });
26
  }
27
 
@@ -55,18 +54,14 @@
55
 
56
  function formatName(provider: string) {
57
  if (provider in nameMap) return nameMap[provider];
58
-
59
  const words = provider
60
  .toLowerCase()
61
  .split("-")
62
- .map(word => {
63
- if (UPPERCASE_WORDS.includes(word)) {
64
- return word.toUpperCase();
65
- } else {
66
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
67
- }
68
- });
69
-
70
  return words.join(" ");
71
  }
72
 
@@ -101,140 +96,95 @@
101
  conversation.update({ autoPolicy: v });
102
  },
103
  });
 
 
 
 
 
 
 
 
 
104
  </script>
105
 
106
- {#snippet providerDisplay(provider: string, showPricing: boolean = true)}
107
  {@const providerPricing = getProviderPricing(provider)}
108
- <div class="flex flex-col items-start gap-0.5">
109
- <div class="flex items-center gap-2 text-sm">
110
- <IconProvider {provider} />
111
- <span>{getProviderName(provider) ?? "loading"}</span>
 
 
 
 
 
 
 
112
  </div>
113
- {#if showPricing && providerPricing}
114
- <span class="text-xs text-gray-500 dark:text-gray-400">
115
- In: {providerPricing.input} • Out: {providerPricing.output}
116
- </span>
117
- {/if}
118
  </div>
119
  {/snippet}
120
 
121
- {#snippet compactProviderDisplay(provider: string)}
122
- <div class="flex items-center gap-1.5">
123
- <IconProvider {provider} />
124
- <span class="text-sm">{getProviderName(provider) ?? "loading"}</span>
 
 
125
  </div>
126
  {/snippet}
127
 
128
  {#if compact}
129
- <!-- Compact mode for top bar - provider and auto policy side by side -->
130
  <div class="flex items-center gap-2">
131
- <!-- Provider select -->
132
- <button
133
- {...select.trigger}
134
- class={cn(
135
- "focus-outline relative flex items-center gap-2 overflow-hidden rounded-lg border",
136
- "bg-gray-100/80 px-3 py-1.5 text-sm leading-tight whitespace-nowrap shadow-sm",
137
- "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
138
- classes,
139
- )}
140
- >
141
- {@render compactProviderDisplay(conversation.data.provider ?? "")}
142
  <IconCaret class="size-4 flex-none text-gray-500" />
143
  </button>
144
 
145
- <div {...select.content} class="z-50 rounded-lg border bg-gray-100 dark:border-gray-700 dark:bg-gray-800">
146
- {#snippet option(provider: string)}
147
- <div {...select.getOption(provider)} class="group block w-full p-1 text-sm dark:text-white">
148
- <div
149
- class="rounded-md px-2 py-1.5 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700"
150
- >
151
- {@render providerDisplay(provider)}
152
- </div>
153
- </div>
154
- {/snippet}
155
  {#each conversation.model.inferenceProviderMapping as { provider, providerId } (provider + providerId)}
156
- {@render option(provider)}
157
  {/each}
158
- {@render option("auto")}
159
  </div>
160
 
161
- <!-- Auto policy select (only shown when provider is auto) -->
162
  {#if conversation.data.provider === "auto"}
163
  <Tooltip>
164
  {#snippet trigger(tooltip)}
165
- <button
166
- {...autoPolicySelect.trigger}
167
- class={cn(
168
- "focus-outline relative flex items-center gap-2 overflow-hidden rounded-lg border",
169
- "bg-gray-100/80 px-3 py-1.5 text-sm leading-tight whitespace-nowrap shadow-sm",
170
- "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
171
- )}
172
- {...tooltip.trigger}
173
- >
174
- {autoPolicyLabels[autoPolicyValue]}
175
  <IconCaret class="size-4 flex-none text-gray-500" />
176
  </button>
177
  {/snippet}
178
  {autoPolicyDescriptions[autoPolicyValue]}
179
  </Tooltip>
180
 
181
- <div
182
- {...autoPolicySelect.content}
183
- class="z-50 rounded-lg border bg-gray-100 dark:border-gray-700 dark:bg-gray-800"
184
- >
185
- {#snippet policyOption(policy: "default" | "fastest" | "cheapest", label: string)}
186
- <div {...autoPolicySelect.getOption(policy)} class="group block w-full p-1 text-sm dark:text-white">
187
- <div
188
- class="rounded-md px-2 py-1.5 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700"
189
- >
190
- <div class="flex flex-col items-start gap-0.5">
191
- <span>{label}</span>
192
- <span class="text-xs text-gray-500 dark:text-gray-400">
193
- {autoPolicyDescriptions[policy]}
194
- </span>
195
- </div>
196
- </div>
197
- </div>
198
- {/snippet}
199
- {@render policyOption("default", "Default")}
200
- {@render policyOption("fastest", "Fastest")}
201
- {@render policyOption("cheapest", "Cheapest")}
202
  </div>
203
  {/if}
204
  </div>
205
  {:else}
206
  <!-- Full mode for settings panel -->
207
  <div class="flex flex-col gap-2">
208
- <button
209
- {...select.trigger}
210
- class={cn(
211
- "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",
212
- "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
213
- classes,
214
- )}
215
- >
216
- {@render providerDisplay(conversation.data.provider ?? "")}
217
- <div
218
- class="absolute right-2 grid size-4 flex-none place-items-center rounded-sm bg-gray-100 text-xs dark:bg-gray-600"
219
- >
220
- <IconCaret />
221
  </div>
 
222
  </button>
223
 
224
- <div {...select.content} class="rounded-lg border bg-gray-100 dark:border-gray-700 dark:bg-gray-800">
225
- {#snippet option(provider: string)}
226
- <div {...select.getOption(provider)} class="group block w-full p-1 text-sm dark:text-white">
227
- <div
228
- class="rounded-md px-2 py-1.5 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700"
229
- >
230
- {@render providerDisplay(provider)}
231
- </div>
232
- </div>
233
- {/snippet}
234
  {#each conversation.model.inferenceProviderMapping as { provider, providerId } (provider + providerId)}
235
- {@render option(provider)}
236
  {/each}
237
- {@render option("auto")}
238
  </div>
239
 
240
  {#if conversation.data.provider === "auto"}
@@ -250,39 +200,15 @@
250
  {autoPolicyDescriptions[autoPolicyValue]}
251
  </Tooltip>
252
  </div>
253
- <button
254
- {...autoPolicySelect.trigger}
255
- class={cn(
256
- "relative flex items-center justify-between gap-6 overflow-hidden rounded-lg border bg-gray-100/80 px-3 py-1.5 text-sm leading-tight whitespace-nowrap shadow-sm",
257
- "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
258
- )}
259
- >
260
- {autoPolicyLabels[autoPolicyValue]}
261
- <div
262
- class="absolute right-2 grid size-4 flex-none place-items-center rounded-sm bg-gray-100 text-xs dark:bg-gray-600"
263
- >
264
- <IconCaret />
265
- </div>
266
  </button>
267
 
268
- <div {...autoPolicySelect.content} class="rounded-lg border bg-gray-100 dark:border-gray-700 dark:bg-gray-800">
269
- {#snippet policyOption(policy: "default" | "fastest" | "cheapest", label: string)}
270
- <div {...autoPolicySelect.getOption(policy)} class="group block w-full p-1 text-sm dark:text-white">
271
- <div
272
- class="rounded-md px-2 py-1.5 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700"
273
- >
274
- <div class="flex flex-col items-start gap-0.5">
275
- <span>{label}</span>
276
- <span class="text-xs text-gray-500 dark:text-gray-400">
277
- {autoPolicyDescriptions[policy]}
278
- </span>
279
- </div>
280
- </div>
281
- </div>
282
- {/snippet}
283
- {@render policyOption("default", "Default")}
284
- {@render policyOption("fastest", "Fastest")}
285
- {@render policyOption("cheapest", "Cheapest")}
286
  </div>
287
  </div>
288
  {/if}
 
21
  function reset(providers: typeof conversation.model.inferenceProviderMapping) {
22
  const validProvider = providers.find(p => p.provider === conversation.data.provider);
23
  if (validProvider || conversation.data.provider === "auto") return;
 
24
  conversation.update({ provider: "auto", autoPolicy: "default" });
25
  }
26
 
 
54
 
55
  function formatName(provider: string) {
56
  if (provider in nameMap) return nameMap[provider];
 
57
  const words = provider
58
  .toLowerCase()
59
  .split("-")
60
+ .map(word =>
61
+ UPPERCASE_WORDS.includes(word)
62
+ ? word.toUpperCase()
63
+ : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
64
+ );
 
 
 
65
  return words.join(" ");
66
  }
67
 
 
96
  conversation.update({ autoPolicy: v });
97
  },
98
  });
99
+
100
+ // Shared button styles
101
+ const triggerClass = cn(
102
+ "focus-outline relative flex items-center justify-between overflow-hidden rounded-lg border",
103
+ "bg-gray-100/80 px-3 py-1.5 text-sm leading-tight whitespace-nowrap shadow-sm",
104
+ "hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
105
+ );
106
+
107
+ const dropdownClass = "z-50 rounded-lg border bg-gray-100 dark:border-gray-700 dark:bg-gray-800";
108
  </script>
109
 
110
+ {#snippet providerOption(provider: string, showPricing: boolean = true)}
111
  {@const providerPricing = getProviderPricing(provider)}
112
+ <div {...select.getOption(provider)} class="group block w-full p-1 text-sm dark:text-white">
113
+ <div class="rounded-md px-2 py-1.5 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700">
114
+ <div class="flex items-center gap-2">
115
+ <IconProvider {provider} />
116
+ <span>{getProviderName(provider)}</span>
117
+ </div>
118
+ {#if showPricing && providerPricing}
119
+ <span class="text-xs text-gray-500 dark:text-gray-400">
120
+ In: {providerPricing.input} • Out: {providerPricing.output}
121
+ </span>
122
+ {/if}
123
  </div>
 
 
 
 
 
124
  </div>
125
  {/snippet}
126
 
127
+ {#snippet policyOption(policy: AutoPolicy)}
128
+ <div {...autoPolicySelect.getOption(policy)} class="group block w-full p-1 text-sm dark:text-white">
129
+ <div class="rounded-md px-2 py-1.5 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700">
130
+ <span>{autoPolicyLabels[policy]}</span>
131
+ <span class="block text-xs text-gray-500 dark:text-gray-400">{autoPolicyDescriptions[policy]}</span>
132
+ </div>
133
  </div>
134
  {/snippet}
135
 
136
  {#if compact}
137
+ <!-- Compact mode: provider and auto-policy side by side, filling width -->
138
  <div class="flex items-center gap-2">
139
+ <button {...select.trigger} class={cn(triggerClass, "flex-1", classes)}>
140
+ <div class="flex items-center gap-1.5">
141
+ <IconProvider provider={conversation.data.provider ?? ""} />
142
+ <span>{getProviderName(conversation.data.provider ?? "")}</span>
143
+ </div>
 
 
 
 
 
 
144
  <IconCaret class="size-4 flex-none text-gray-500" />
145
  </button>
146
 
147
+ <div {...select.content} class={dropdownClass}>
 
 
 
 
 
 
 
 
 
148
  {#each conversation.model.inferenceProviderMapping as { provider, providerId } (provider + providerId)}
149
+ {@render providerOption(provider)}
150
  {/each}
151
+ {@render providerOption("auto")}
152
  </div>
153
 
 
154
  {#if conversation.data.provider === "auto"}
155
  <Tooltip>
156
  {#snippet trigger(tooltip)}
157
+ <button {...autoPolicySelect.trigger} class={cn(triggerClass, "flex-1")} {...tooltip.trigger}>
158
+ <span>{autoPolicyLabels[autoPolicyValue]}</span>
 
 
 
 
 
 
 
 
159
  <IconCaret class="size-4 flex-none text-gray-500" />
160
  </button>
161
  {/snippet}
162
  {autoPolicyDescriptions[autoPolicyValue]}
163
  </Tooltip>
164
 
165
+ <div {...autoPolicySelect.content} class={dropdownClass}>
166
+ {@render policyOption("default")}
167
+ {@render policyOption("fastest")}
168
+ {@render policyOption("cheapest")}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  </div>
170
  {/if}
171
  </div>
172
  {:else}
173
  <!-- Full mode for settings panel -->
174
  <div class="flex flex-col gap-2">
175
+ <button {...select.trigger} class={cn(triggerClass, classes)}>
176
+ <div class="flex items-center gap-2">
177
+ <IconProvider provider={conversation.data.provider ?? ""} />
178
+ <span>{getProviderName(conversation.data.provider ?? "")}</span>
 
 
 
 
 
 
 
 
 
179
  </div>
180
+ <IconCaret class="size-4 flex-none text-gray-500" />
181
  </button>
182
 
183
+ <div {...select.content} class={dropdownClass}>
 
 
 
 
 
 
 
 
 
184
  {#each conversation.model.inferenceProviderMapping as { provider, providerId } (provider + providerId)}
185
+ {@render providerOption(provider)}
186
  {/each}
187
+ {@render providerOption("auto")}
188
  </div>
189
 
190
  {#if conversation.data.provider === "auto"}
 
200
  {autoPolicyDescriptions[autoPolicyValue]}
201
  </Tooltip>
202
  </div>
203
+ <button {...autoPolicySelect.trigger} class={triggerClass}>
204
+ <span>{autoPolicyLabels[autoPolicyValue]}</span>
205
+ <IconCaret class="size-4 flex-none text-gray-500" />
 
 
 
 
 
 
 
 
 
 
206
  </button>
207
 
208
+ <div {...autoPolicySelect.content} class={dropdownClass}>
209
+ {@render policyOption("default")}
210
+ {@render policyOption("fastest")}
211
+ {@render policyOption("cheapest")}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  </div>
213
  </div>
214
  {/if}