Spaces:
				
			
			
	
			
			
		Runtime error
		
	
	
	
			
			
	
	
	
	
		
		
		Runtime error
		
	
		Thomas G. Lopes
		
	commited on
		
		
					Commit 
							
							·
						
						d7d2be5
	
1
								Parent(s):
							
							bee7674
								
images upload
Browse files
    	
        src/routes/canvas/+page.svelte
    CHANGED
    
    | @@ -8,6 +8,7 @@ | |
| 8 | 
             
            	import ChatNode from "./chat-node.svelte";
         | 
| 9 | 
             
            	import { edges, nodes } from "./state.js";
         | 
| 10 | 
             
            	import { models } from "$lib/state/models.svelte";
         | 
|  | |
| 11 |  | 
| 12 | 
             
            	const nodeTypes = { chat: ChatNode } as const;
         | 
| 13 |  | 
| @@ -80,3 +81,5 @@ | |
| 80 | 
             
            		</SvelteFlow>
         | 
| 81 | 
             
            	{/if}
         | 
| 82 | 
             
            </div>
         | 
|  | |
|  | 
|  | |
| 8 | 
             
            	import ChatNode from "./chat-node.svelte";
         | 
| 9 | 
             
            	import { edges, nodes } from "./state.js";
         | 
| 10 | 
             
            	import { models } from "$lib/state/models.svelte";
         | 
| 11 | 
            +
            	import ImgPreview from "$lib/components/inference-playground/img-preview.svelte";
         | 
| 12 |  | 
| 13 | 
             
            	const nodeTypes = { chat: ChatNode } as const;
         | 
| 14 |  | 
|  | |
| 81 | 
             
            		</SvelteFlow>
         | 
| 82 | 
             
            	{/if}
         | 
| 83 | 
             
            </div>
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            <ImgPreview />
         | 
    	
        src/routes/canvas/chat-node.svelte
    CHANGED
    
    | @@ -1,28 +1,32 @@ | |
| 1 | 
             
            <script lang="ts">
         | 
|  | |
| 2 | 
             
            	import { TextareaAutosize } from "$lib/spells/textarea-autosize.svelte";
         | 
|  | |
| 3 | 
             
            	import { models } from "$lib/state/models.svelte";
         | 
| 4 | 
             
            	import { token } from "$lib/state/token.svelte";
         | 
| 5 | 
             
            	import type { Model } from "$lib/types.js";
         | 
|  | |
| 6 | 
             
            	import { InferenceClient } from "@huggingface/inference";
         | 
|  | |
| 7 | 
             
            	import { Handle, Position, useSvelteFlow, type Edge, type Node, type NodeProps } from "@xyflow/svelte";
         | 
|  | |
|  | |
|  | |
| 8 | 
             
            	import { onMount } from "svelte";
         | 
| 9 | 
            -
            	import {  | 
| 10 | 
            -
            	import  | 
| 11 | 
            -
            	import  | 
| 12 | 
            -
            	import  | 
| 13 | 
            -
            	import IconStop from "~icons/lucide/square";
         | 
| 14 | 
             
            	import IconCopy from "~icons/lucide/copy";
         | 
|  | |
| 15 | 
             
            	import IconAttachment from "~icons/lucide/paperclip";
         | 
| 16 | 
            -
            	import  | 
| 17 | 
             
            	import IconSend from "~icons/lucide/send";
         | 
| 18 | 
            -
            	import  | 
|  | |
| 19 | 
             
            	import ModelPicker from "./model-picker.svelte";
         | 
| 20 | 
             
            	import ProviderPicker from "./provider-picker.svelte";
         | 
| 21 | 
            -
            	import {  | 
| 22 | 
            -
            	import { marked } from "marked";
         | 
| 23 | 
            -
            	import { images } from "$lib/state/images.svelte.js";
         | 
| 24 | 
            -
            	import { AsyncQueue } from "$lib/utils/queue.js";
         | 
| 25 | 
            -
            	import { FileUpload } from "melt/builders";
         | 
| 26 |  | 
| 27 | 
             
            	type Props = Omit<NodeProps, "data"> & {
         | 
| 28 | 
             
            		data: { query: string; response: string; modelId?: Model["id"]; provider?: string; imageIds?: string[] };
         | 
| @@ -315,7 +319,17 @@ | |
| 315 | 
             
            			<!-- Message container with top bar, textarea, and bottom bar -->
         | 
| 316 | 
             
            			<div
         | 
| 317 | 
             
            				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"
         | 
|  | |
| 318 | 
             
            			>
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 319 | 
             
            				<!-- Top bar with buttons and character count -->
         | 
| 320 | 
             
            				<div class="flex items-center justify-between border-b border-gray-200 px-4 py-2">
         | 
| 321 | 
             
            					<div class="flex items-center gap-2">
         | 
| @@ -323,8 +337,10 @@ | |
| 323 | 
             
            							type="button"
         | 
| 324 | 
             
            							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"
         | 
| 325 | 
             
            							title="Attach file"
         | 
|  | |
| 326 | 
             
            						>
         | 
| 327 | 
             
            							<IconAttachment class="h-4 w-4" />
         | 
|  | |
| 328 | 
             
            						</button>
         | 
| 329 | 
             
            						<button
         | 
| 330 | 
             
            							type="button"
         | 
| @@ -355,6 +371,39 @@ | |
| 355 | 
             
            			</div>
         | 
| 356 | 
             
            		</div>
         | 
| 357 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 358 | 
             
            		<!-- Bottom bar with hint and send button (outside textarea) -->
         | 
| 359 | 
             
            		<div class="mt-2 flex items-center justify-between">
         | 
| 360 | 
             
            			<div class="text-xs text-gray-500">Shift + Enter for newline</div>
         | 
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
            +
            	import { previewImage } from "$lib/components/inference-playground/img-preview.svelte";
         | 
| 3 | 
             
            	import { TextareaAutosize } from "$lib/spells/textarea-autosize.svelte";
         | 
| 4 | 
            +
            	import { images } from "$lib/state/images.svelte.js";
         | 
| 5 | 
             
            	import { models } from "$lib/state/models.svelte";
         | 
| 6 | 
             
            	import { token } from "$lib/state/token.svelte";
         | 
| 7 | 
             
            	import type { Model } from "$lib/types.js";
         | 
| 8 | 
            +
            	import { AsyncQueue } from "$lib/utils/queue.js";
         | 
| 9 | 
             
            	import { InferenceClient } from "@huggingface/inference";
         | 
| 10 | 
            +
            	import type { ChatCompletionInputMessage } from "@huggingface/tasks";
         | 
| 11 | 
             
            	import { Handle, Position, useSvelteFlow, type Edge, type Node, type NodeProps } from "@xyflow/svelte";
         | 
| 12 | 
            +
            	import { marked } from "marked";
         | 
| 13 | 
            +
            	import { FileUpload } from "melt/builders";
         | 
| 14 | 
            +
            	import { ElementSize } from "runed";
         | 
| 15 | 
             
            	import { onMount } from "svelte";
         | 
| 16 | 
            +
            	import { fade } from "svelte/transition";
         | 
| 17 | 
            +
            	import IconImage from "~icons/carbon/image-reference";
         | 
| 18 | 
            +
            	import IconMaximize from "~icons/carbon/maximize";
         | 
| 19 | 
            +
            	import IconCode from "~icons/lucide/code";
         | 
|  | |
| 20 | 
             
            	import IconCopy from "~icons/lucide/copy";
         | 
| 21 | 
            +
            	import IconLoading from "~icons/lucide/loader-2";
         | 
| 22 | 
             
            	import IconAttachment from "~icons/lucide/paperclip";
         | 
| 23 | 
            +
            	import IconAdd from "~icons/lucide/plus";
         | 
| 24 | 
             
            	import IconSend from "~icons/lucide/send";
         | 
| 25 | 
            +
            	import IconStop from "~icons/lucide/square";
         | 
| 26 | 
            +
            	import IconX from "~icons/lucide/x";
         | 
| 27 | 
             
            	import ModelPicker from "./model-picker.svelte";
         | 
| 28 | 
             
            	import ProviderPicker from "./provider-picker.svelte";
         | 
| 29 | 
            +
            	import { edges, nodes } from "./state.js";
         | 
|  | |
|  | |
|  | |
|  | |
| 30 |  | 
| 31 | 
             
            	type Props = Omit<NodeProps, "data"> & {
         | 
| 32 | 
             
            		data: { query: string; response: string; modelId?: Model["id"]; provider?: string; imageIds?: string[] };
         | 
|  | |
| 319 | 
             
            			<!-- Message container with top bar, textarea, and bottom bar -->
         | 
| 320 | 
             
            			<div
         | 
| 321 | 
             
            				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"
         | 
| 322 | 
            +
            				{...fileUpload.dropzone}
         | 
| 323 | 
             
            			>
         | 
| 324 | 
            +
            				{#if fileUpload.isDragging}
         | 
| 325 | 
            +
            					<div
         | 
| 326 | 
            +
            						class="absolute inset-2 z-10 flex flex-col items-center justify-center rounded-xl bg-gray-800/50 backdrop-blur-md"
         | 
| 327 | 
            +
            						transition:fade={{ duration: 100 }}
         | 
| 328 | 
            +
            					>
         | 
| 329 | 
            +
            						<IconImage />
         | 
| 330 | 
            +
            						<p>Drop the image here to upload</p>
         | 
| 331 | 
            +
            					</div>
         | 
| 332 | 
            +
            				{/if}
         | 
| 333 | 
             
            				<!-- Top bar with buttons and character count -->
         | 
| 334 | 
             
            				<div class="flex items-center justify-between border-b border-gray-200 px-4 py-2">
         | 
| 335 | 
             
            					<div class="flex items-center gap-2">
         | 
|  | |
| 337 | 
             
            							type="button"
         | 
| 338 | 
             
            							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"
         | 
| 339 | 
             
            							title="Attach file"
         | 
| 340 | 
            +
            							{...fileUpload.trigger}
         | 
| 341 | 
             
            						>
         | 
| 342 | 
             
            							<IconAttachment class="h-4 w-4" />
         | 
| 343 | 
            +
            							<input {...fileUpload.input} />
         | 
| 344 | 
             
            						</button>
         | 
| 345 | 
             
            						<button
         | 
| 346 | 
             
            							type="button"
         | 
|  | |
| 371 | 
             
            			</div>
         | 
| 372 | 
             
            		</div>
         | 
| 373 |  | 
| 374 | 
            +
            		<div class="mt-2">
         | 
| 375 | 
            +
            			<div class="flex items-center gap-2">
         | 
| 376 | 
            +
            				{#each data.imageIds ?? [] as imgKey (imgKey)}
         | 
| 377 | 
            +
            					{#await images.get(imgKey)}
         | 
| 378 | 
            +
            						<!-- nothing -->
         | 
| 379 | 
            +
            					{:then imgSrc}
         | 
| 380 | 
            +
            						<div class="group/img relative">
         | 
| 381 | 
            +
            							<button
         | 
| 382 | 
            +
            								aria-label="expand"
         | 
| 383 | 
            +
            								class="absolute inset-0 z-10 grid place-items-center rounded-md bg-gray-800/70 text-white opacity-0 group-hover/img:opacity-100"
         | 
| 384 | 
            +
            								onclick={() => previewImage(imgSrc)}
         | 
| 385 | 
            +
            							>
         | 
| 386 | 
            +
            								<IconMaximize />
         | 
| 387 | 
            +
            							</button>
         | 
| 388 | 
            +
            							<img src={imgSrc} alt="uploaded" class="size-12 rounded-md object-cover" />
         | 
| 389 | 
            +
            							<button
         | 
| 390 | 
            +
            								aria-label="remove"
         | 
| 391 | 
            +
            								type="button"
         | 
| 392 | 
            +
            								onclick={async e => {
         | 
| 393 | 
            +
            									e.stopPropagation();
         | 
| 394 | 
            +
            									updateNodeData(id, { imageIds: data.imageIds?.filter(i => i !== imgKey) });
         | 
| 395 | 
            +
            									images.delete(imgKey);
         | 
| 396 | 
            +
            								}}
         | 
| 397 | 
            +
            								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"
         | 
| 398 | 
            +
            							>
         | 
| 399 | 
            +
            								✕
         | 
| 400 | 
            +
            							</button>
         | 
| 401 | 
            +
            						</div>
         | 
| 402 | 
            +
            					{/await}
         | 
| 403 | 
            +
            				{/each}
         | 
| 404 | 
            +
            			</div>
         | 
| 405 | 
            +
            		</div>
         | 
| 406 | 
            +
             | 
| 407 | 
             
            		<!-- Bottom bar with hint and send button (outside textarea) -->
         | 
| 408 | 
             
            		<div class="mt-2 flex items-center justify-between">
         | 
| 409 | 
             
            			<div class="text-xs text-gray-500">Shift + Enter for newline</div>
         | 
