Thomas G. Lopes commited on
Commit
28afdf5
·
1 Parent(s): 00d40c6

fix msg layout

Browse files
src/lib/components/inference-playground/message.svelte CHANGED
@@ -117,63 +117,64 @@
117
  {message?.role}
118
  </div>
119
 
120
- <div class="flex w-full flex-col gap-2">
121
- <!-- Reasoning section (if present) -->
122
- {#if parsedMessage.thinking && message?.role === "assistant"}
123
- <div class="flex w-full flex-col gap-2">
124
- <button
125
- onclick={() => (isReasoningExpanded = !isReasoningExpanded)}
126
- class="flex items-center gap-2 self-start rounded-md px-2 py-1 text-sm font-medium text-gray-600 hover:bg-gray-100 hover:text-gray-800 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
127
- >
128
- {#if isReasoningExpanded}
129
- <IconChevronDown class="size-4" />
130
- {:else}
131
- <IconChevronRight class="size-4" />
132
- {/if}
133
- Reasoning
134
- </button>
 
 
135
 
136
- {#if isReasoningExpanded}
137
- {#if conversation.data.parseMarkdown && !isEditing}
138
- <div
139
- class="relative w-full max-w-none rounded-lg bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:bg-white @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900"
140
- >
141
- <div class="prose prose-sm dark:prose-invert">
142
- {@html parsedReasoning}
 
143
  </div>
144
- </div>
145
- {:else}
146
- <textarea
147
- value={parsedMessage.thinking}
148
- onchange={e => {
149
- const el = e.target as HTMLTextAreaElement;
150
- const reasoningContent = el?.value ?? "";
151
- if (!message) return;
152
- // Reconstruct the full message with updated reasoning
153
- const newContent = reasoningContent
154
- ? `<think>${reasoningContent}</think>\n\n${parsedMessage.content}`
155
- : parsedMessage.content;
156
- conversation.updateMessage({ index, message: { ...message, content: newContent } });
157
- }}
158
- onkeydown={e => {
159
- if ((e.ctrlKey || e.metaKey) && e.key === "g") {
160
- e.preventDefault();
161
- e.stopPropagation();
162
- onRegen?.();
163
- }
164
- }}
165
- placeholder="Enter reasoning content"
166
- class="w-full resize-none overflow-hidden rounded-lg bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:bg-white focus:bg-white focus:ring-3 @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
167
- rows="1"
168
- {@attach reasoningAutosized.attachment}
169
- ></textarea>
170
  {/if}
171
- {/if}
172
- </div>
173
- {/if}
174
 
175
- <!-- Main content section -->
176
- <div class="flex w-full gap-4">
177
  {#if conversation.data.parseMarkdown && message?.role === "assistant"}
178
  <div
179
  class="relative max-w-none grow rounded-lg bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:bg-white @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900"
@@ -256,6 +257,7 @@
256
  ></textarea>
257
  {/if}
258
  </div>
 
259
  <!-- Sticky wrapper for action buttons -->
260
  <div
261
  class={[
@@ -360,40 +362,40 @@
360
  </div>
361
  </div>
362
  </div>
 
363
 
364
- <div class="mt-2">
365
- <div class="flex items-center gap-2">
366
- {#each message.images ?? [] as imgKey (imgKey)}
367
- {#await images.get(imgKey)}
368
- <!-- nothing -->
369
- {:then imgSrc}
370
- <div class="group/img relative">
371
- <button
372
- aria-label="expand"
373
- class="absolute inset-0 z-10 grid place-items-center bg-gray-800/70 opacity-0 group-hover/img:opacity-100"
374
- onclick={() => previewImage(imgSrc)}
375
- >
376
- <IconMaximize />
377
- </button>
378
- <img src={imgSrc} alt="uploaded" class="size-12 rounded-md object-cover" />
379
- <button
380
- aria-label="remove"
381
- type="button"
382
- onclick={async e => {
383
- e.stopPropagation();
384
- await conversation.updateMessage({
385
- index,
386
- message: { images: message.images?.filter(i => i !== imgKey) },
387
- });
388
- images.delete(imgKey);
389
- }}
390
- class="invisible absolute -top-1 -right-1 z-20 grid size-5 place-items-center rounded-full bg-gray-800 text-xs text-white group-hover/img:visible hover:bg-gray-700"
391
- >
392
-
393
- </button>
394
- </div>
395
- {/await}
396
- {/each}
397
- </div>
398
  </div>
399
  </div>
 
117
  {message?.role}
118
  </div>
119
 
120
+ <div class="flex w-full gap-4">
121
+ <!-- Content column (reasoning + main content) -->
122
+ <div class="flex w-full flex-col gap-2">
123
+ <!-- Reasoning section (if present) -->
124
+ {#if parsedMessage.thinking && message?.role === "assistant"}
125
+ <div class="flex w-full flex-col gap-2">
126
+ <button
127
+ onclick={() => (isReasoningExpanded = !isReasoningExpanded)}
128
+ class="flex items-center gap-2 self-start rounded-md px-2 py-1 text-sm font-medium text-gray-600 hover:bg-gray-100 hover:text-gray-800 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
129
+ >
130
+ {#if isReasoningExpanded}
131
+ <IconChevronDown class="size-4" />
132
+ {:else}
133
+ <IconChevronRight class="size-4" />
134
+ {/if}
135
+ Reasoning
136
+ </button>
137
 
138
+ {#if isReasoningExpanded}
139
+ {#if conversation.data.parseMarkdown && !isEditing}
140
+ <div
141
+ class="relative w-full max-w-none rounded-lg bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:bg-white @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900"
142
+ >
143
+ <div class="prose prose-sm dark:prose-invert">
144
+ {@html parsedReasoning}
145
+ </div>
146
  </div>
147
+ {:else}
148
+ <textarea
149
+ value={parsedMessage.thinking}
150
+ onchange={e => {
151
+ const el = e.target as HTMLTextAreaElement;
152
+ const reasoningContent = el?.value ?? "";
153
+ if (!message) return;
154
+ // Reconstruct the full message with updated reasoning
155
+ const newContent = reasoningContent
156
+ ? `<think>${reasoningContent}</think>\n\n${parsedMessage.content}`
157
+ : parsedMessage.content;
158
+ conversation.updateMessage({ index, message: { ...message, content: newContent } });
159
+ }}
160
+ onkeydown={e => {
161
+ if ((e.ctrlKey || e.metaKey) && e.key === "g") {
162
+ e.preventDefault();
163
+ e.stopPropagation();
164
+ onRegen?.();
165
+ }
166
+ }}
167
+ placeholder="Enter reasoning content"
168
+ class="w-full resize-none overflow-hidden rounded-lg bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:bg-white focus:bg-white focus:ring-3 @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
169
+ rows="1"
170
+ {@attach reasoningAutosized.attachment}
171
+ ></textarea>
172
+ {/if}
173
  {/if}
174
+ </div>
175
+ {/if}
 
176
 
177
+ <!-- Main content section -->
 
178
  {#if conversation.data.parseMarkdown && message?.role === "assistant"}
179
  <div
180
  class="relative max-w-none grow rounded-lg bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:bg-white @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900"
 
257
  ></textarea>
258
  {/if}
259
  </div>
260
+
261
  <!-- Sticky wrapper for action buttons -->
262
  <div
263
  class={[
 
362
  </div>
363
  </div>
364
  </div>
365
+ </div>
366
 
367
+ <div class="mt-2">
368
+ <div class="flex items-center gap-2">
369
+ {#each message.images ?? [] as imgKey (imgKey)}
370
+ {#await images.get(imgKey)}
371
+ <!-- nothing -->
372
+ {:then imgSrc}
373
+ <div class="group/img relative">
374
+ <button
375
+ aria-label="expand"
376
+ class="absolute inset-0 z-10 grid place-items-center bg-gray-800/70 opacity-0 group-hover/img:opacity-100"
377
+ onclick={() => previewImage(imgSrc)}
378
+ >
379
+ <IconMaximize />
380
+ </button>
381
+ <img src={imgSrc} alt="uploaded" class="size-12 rounded-md object-cover" />
382
+ <button
383
+ aria-label="remove"
384
+ type="button"
385
+ onclick={async e => {
386
+ e.stopPropagation();
387
+ await conversation.updateMessage({
388
+ index,
389
+ message: { images: message.images?.filter(i => i !== imgKey) },
390
+ });
391
+ images.delete(imgKey);
392
+ }}
393
+ class="invisible absolute -top-1 -right-1 z-20 grid size-5 place-items-center rounded-full bg-gray-800 text-xs text-white group-hover/img:visible hover:bg-gray-700"
394
+ >
395
+
396
+ </button>
397
+ </div>
398
+ {/await}
399
+ {/each}
 
400
  </div>
401
  </div>