Thomas G. Lopes commited on
Commit
43b907b
·
1 Parent(s): aecbec0

new appearance

Browse files
Files changed (1) hide show
  1. src/routes/canvas/chat-node.svelte +81 -38
src/routes/canvas/chat-node.svelte CHANGED
@@ -12,6 +12,9 @@
12
  import IconX from "~icons/lucide/x";
13
  import IconStop from "~icons/lucide/square";
14
  import IconCopy from "~icons/lucide/copy";
 
 
 
15
  import type { ChatCompletionInputMessage } from "@huggingface/tasks";
16
  import ModelPicker from "./model-picker.svelte";
17
  import ProviderPicker from "./provider-picker.svelte";
@@ -141,6 +144,8 @@
141
  return marked(data.response);
142
  });
143
 
 
 
144
  // Helper function to get actual node dimensions from DOM, converted to flow coordinates
145
  function getNodeDimensions(nodeId: string) {
146
  const nodeElement = document.querySelector(`[data-id="${nodeId}"]`);
@@ -247,53 +252,91 @@
247
  </script>
248
 
249
  <div
250
- class="chat-node group relative flex h-full min-h-[200px] w-full max-w-[800px]
251
- min-w-[500px] flex-col items-stretch rounded-2xl border border-gray-200 bg-white p-6 shadow-sm"
252
  bind:this={node}
253
  >
254
  <!-- Model and Provider selectors -->
255
- <div class="mb-4 space-y-3">
256
- <ModelPicker modelId={data.modelId} onModelSelect={modelId => updateNodeData(id, { modelId })} />
257
- <ProviderPicker
258
- provider={data.provider}
259
- modelId={data.modelId}
260
- onProviderSelect={provider => updateNodeData(id, { provider })}
261
- />
 
 
 
 
262
  </div>
263
 
264
- <form class="flex flex-col gap-4" onsubmit={handleSubmit}>
265
  <div class="relative">
266
  <label for={`message-${id}`} class="mb-1.5 block text-xs font-medium text-gray-600">Message</label>
267
- <textarea
268
- id={`message-${id}`}
269
- class="nodrag min-h-[80px] w-full resize-none rounded-lg border border-gray-200 bg-gray-50 px-4
270
- py-3 text-sm text-gray-900 placeholder-gray-500 transition-colors
271
- focus:border-gray-900 focus:ring-2 focus:ring-gray-900/10 focus:outline-none"
272
- placeholder="Type your message here..."
273
- value={data.query}
274
- oninput={evt => {
275
- updateNodeData(id, { query: evt.currentTarget.value });
276
- }}
277
- {@attach autosized.attachment}
278
- ></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  </div>
280
 
281
- <button
282
- type={isLoading ? "button" : "submit"}
283
- onclick={isLoading ? stopGeneration : undefined}
284
- class="flex items-center justify-center gap-2 self-center rounded-xl
285
- bg-black px-6 py-2.5 text-sm font-medium
286
- text-white transition-all hover:scale-[1.02] hover:bg-gray-900
287
- focus:ring-2 focus:ring-gray-900/20 focus:outline-none
288
- active:scale-[0.98]"
289
- >
290
- {#if isLoading}
291
- <IconStop class="h-4 w-4" />
292
- Stop Generation
293
- {:else}
294
- Send Message
295
- {/if}
296
- </button>
 
 
 
 
 
297
  </form>
298
 
299
  {#if data.response || isLoading}
 
12
  import IconX from "~icons/lucide/x";
13
  import IconStop from "~icons/lucide/square";
14
  import IconCopy from "~icons/lucide/copy";
15
+ import IconAttachment from "~icons/lucide/paperclip";
16
+ import IconCode from "~icons/lucide/code";
17
+ import IconSend from "~icons/lucide/send";
18
  import type { ChatCompletionInputMessage } from "@huggingface/tasks";
19
  import ModelPicker from "./model-picker.svelte";
20
  import ProviderPicker from "./provider-picker.svelte";
 
144
  return marked(data.response);
145
  });
146
 
147
+ const characterCount = $derived(data.query?.length || 0);
148
+
149
  // Helper function to get actual node dimensions from DOM, converted to flow coordinates
150
  function getNodeDimensions(nodeId: string) {
151
  const nodeElement = document.querySelector(`[data-id="${nodeId}"]`);
 
252
  </script>
253
 
254
  <div
255
+ class="chat-node group relative flex h-full min-h-[200px] w-full max-w-[1000px]
256
+ min-w-[700px] flex-col items-stretch rounded-2xl border border-gray-200 bg-white p-6 shadow-sm"
257
  bind:this={node}
258
  >
259
  <!-- Model and Provider selectors -->
260
+ <div class="mb-4 flex gap-3">
261
+ <div class="flex-[3]">
262
+ <ModelPicker modelId={data.modelId} onModelSelect={modelId => updateNodeData(id, { modelId })} />
263
+ </div>
264
+ <div class="flex-[2]">
265
+ <ProviderPicker
266
+ provider={data.provider}
267
+ modelId={data.modelId}
268
+ onProviderSelect={provider => updateNodeData(id, { provider })}
269
+ />
270
+ </div>
271
  </div>
272
 
273
+ <form class="flex flex-col" onsubmit={handleSubmit}>
274
  <div class="relative">
275
  <label for={`message-${id}`} class="mb-1.5 block text-xs font-medium text-gray-600">Message</label>
276
+
277
+ <!-- Message container with top bar, textarea, and bottom bar -->
278
+ <div
279
+ class="rounded-lg border border-gray-200 bg-gray-50 focus-within:border-gray-900 focus-within:ring-2 focus-within:ring-gray-900/10"
280
+ >
281
+ <!-- Top bar with buttons and character count -->
282
+ <div class="flex items-center justify-between border-b border-gray-200 px-4 py-2">
283
+ <div class="flex items-center gap-2">
284
+ <button
285
+ type="button"
286
+ class="flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-200 hover:text-gray-900"
287
+ title="Attach file"
288
+ >
289
+ <IconAttachment class="h-4 w-4" />
290
+ </button>
291
+ <button
292
+ type="button"
293
+ class="flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-200 hover:text-gray-900"
294
+ title="Code snippet"
295
+ >
296
+ <IconCode class="h-4 w-4" />
297
+ </button>
298
+ </div>
299
+ <div class="text-xs text-gray-500">
300
+ {characterCount}
301
+ </div>
302
+ </div>
303
+
304
+ <!-- Textarea -->
305
+ <textarea
306
+ id={`message-${id}`}
307
+ class="nodrag min-h-[80px] w-full resize-none border-0 bg-transparent px-4
308
+ py-3 text-sm text-gray-900 placeholder-gray-500 focus:ring-0 focus:outline-none"
309
+ placeholder="Type your message..."
310
+ value={data.query}
311
+ oninput={evt => {
312
+ updateNodeData(id, { query: evt.currentTarget.value });
313
+ }}
314
+ {@attach autosized.attachment}
315
+ ></textarea>
316
+ </div>
317
  </div>
318
 
319
+ <!-- Bottom bar with hint and send button (outside textarea) -->
320
+ <div class="mt-2 flex items-center justify-between">
321
+ <div class="text-xs text-gray-500">Shift + Enter for newline</div>
322
+ <button
323
+ type={isLoading ? "button" : "submit"}
324
+ onclick={isLoading ? stopGeneration : undefined}
325
+ class="flex items-center gap-2 rounded-xl
326
+ bg-black px-4 py-2 text-sm font-medium
327
+ text-white transition-all hover:scale-[1.02] hover:bg-gray-900
328
+ focus:ring-2 focus:ring-gray-900/20 focus:outline-none
329
+ active:scale-[0.98]"
330
+ >
331
+ {#if isLoading}
332
+ <IconStop class="h-4 w-4" />
333
+ Stop
334
+ {:else}
335
+ <IconSend class="h-4 w-4" />
336
+ Send
337
+ {/if}
338
+ </button>
339
+ </div>
340
  </form>
341
 
342
  {#if data.response || isLoading}