Spaces:
Runtime error
Runtime error
Img preview (#70)
Browse files
package.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
| 16 |
"devDependencies": {
|
| 17 |
"@eslint/eslintrc": "^3.3.0",
|
| 18 |
"@eslint/js": "^9.22.0",
|
|
|
|
| 19 |
"@huggingface/hub": "^1.0.1",
|
| 20 |
"@huggingface/inference": "^3.5.1",
|
| 21 |
"@huggingface/tasks": "^0.17.1",
|
|
@@ -36,7 +37,7 @@
|
|
| 36 |
"globals": "^16.0.0",
|
| 37 |
"highlight.js": "^11.10.0",
|
| 38 |
"jiti": "^2.4.2",
|
| 39 |
-
"melt": "^0.
|
| 40 |
"postcss": "^8.4.38",
|
| 41 |
"prettier": "^3.1.1",
|
| 42 |
"prettier-plugin-svelte": "^3.2.6",
|
|
|
|
| 16 |
"devDependencies": {
|
| 17 |
"@eslint/eslintrc": "^3.3.0",
|
| 18 |
"@eslint/js": "^9.22.0",
|
| 19 |
+
"@floating-ui/dom": "^1.6.13",
|
| 20 |
"@huggingface/hub": "^1.0.1",
|
| 21 |
"@huggingface/inference": "^3.5.1",
|
| 22 |
"@huggingface/tasks": "^0.17.1",
|
|
|
|
| 37 |
"globals": "^16.0.0",
|
| 38 |
"highlight.js": "^11.10.0",
|
| 39 |
"jiti": "^2.4.2",
|
| 40 |
+
"melt": "^0.20.2",
|
| 41 |
"postcss": "^8.4.38",
|
| 42 |
"prettier": "^3.1.1",
|
| 43 |
"prettier-plugin-svelte": "^3.2.6",
|
pnpm-lock.yaml
CHANGED
|
@@ -21,6 +21,9 @@ importers:
|
|
| 21 |
'@eslint/js':
|
| 22 |
specifier: ^9.22.0
|
| 23 |
version: 9.22.0
|
|
|
|
|
|
|
|
|
|
| 24 |
'@huggingface/hub':
|
| 25 |
specifier: ^1.0.1
|
| 26 |
version: 1.0.1
|
|
@@ -82,8 +85,8 @@ importers:
|
|
| 82 |
specifier: ^2.4.2
|
| 83 |
version: 2.4.2
|
| 84 |
melt:
|
| 85 |
-
specifier: ^0.
|
| 86 |
-
version: 0.
|
| 87 |
postcss:
|
| 88 |
specifier: ^8.4.38
|
| 89 |
version: 8.5.3
|
|
@@ -1545,8 +1548,8 @@ packages:
|
|
| 1545 |
magic-string@0.30.17:
|
| 1546 |
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
| 1547 |
|
| 1548 |
-
melt@0.
|
| 1549 |
-
resolution: {integrity: sha512-
|
| 1550 |
peerDependencies:
|
| 1551 |
'@floating-ui/dom': ^1.6.0
|
| 1552 |
svelte: ^5.0.0
|
|
@@ -3471,7 +3474,7 @@ snapshots:
|
|
| 3471 |
dependencies:
|
| 3472 |
'@jridgewell/sourcemap-codec': 1.5.0
|
| 3473 |
|
| 3474 |
-
melt@0.
|
| 3475 |
dependencies:
|
| 3476 |
'@floating-ui/dom': 1.6.13
|
| 3477 |
jest-axe: 9.0.0
|
|
|
|
| 21 |
'@eslint/js':
|
| 22 |
specifier: ^9.22.0
|
| 23 |
version: 9.22.0
|
| 24 |
+
'@floating-ui/dom':
|
| 25 |
+
specifier: ^1.6.13
|
| 26 |
+
version: 1.6.13
|
| 27 |
'@huggingface/hub':
|
| 28 |
specifier: ^1.0.1
|
| 29 |
version: 1.0.1
|
|
|
|
| 85 |
specifier: ^2.4.2
|
| 86 |
version: 2.4.2
|
| 87 |
melt:
|
| 88 |
+
specifier: ^0.20.2
|
| 89 |
+
version: 0.20.2(@floating-ui/dom@1.6.13)(svelte@5.23.0)
|
| 90 |
postcss:
|
| 91 |
specifier: ^8.4.38
|
| 92 |
version: 8.5.3
|
|
|
|
| 1548 |
magic-string@0.30.17:
|
| 1549 |
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
| 1550 |
|
| 1551 |
+
melt@0.20.2:
|
| 1552 |
+
resolution: {integrity: sha512-vINBKFVqFN8LVU6K7AbEwlGTOzSi5BQulxhKkIroxPWnCA3LVsQEIDZabtf9TtnzFa/th0Rm8J0N+hW1v7VJDw==}
|
| 1553 |
peerDependencies:
|
| 1554 |
'@floating-ui/dom': ^1.6.0
|
| 1555 |
svelte: ^5.0.0
|
|
|
|
| 3474 |
dependencies:
|
| 3475 |
'@jridgewell/sourcemap-codec': 1.5.0
|
| 3476 |
|
| 3477 |
+
melt@0.20.2(@floating-ui/dom@1.6.13)(svelte@5.23.0):
|
| 3478 |
dependencies:
|
| 3479 |
'@floating-ui/dom': 1.6.13
|
| 3480 |
jest-axe: 9.0.0
|
src/lib/components/inference-playground/img-preview.svelte
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { clickOutside } from "$lib/actions/click-outside.js";
|
| 3 |
+
import { fade, scale } from "svelte/transition";
|
| 4 |
+
import IconCross from "~icons/carbon/close";
|
| 5 |
+
|
| 6 |
+
interface Props {
|
| 7 |
+
img?: string;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
let { img = $bindable() }: Props = $props();
|
| 11 |
+
|
| 12 |
+
let dialog: HTMLDialogElement | undefined = $state();
|
| 13 |
+
|
| 14 |
+
$effect(() => {
|
| 15 |
+
if (img) {
|
| 16 |
+
dialog?.showModal();
|
| 17 |
+
} else {
|
| 18 |
+
setTimeout(() => dialog?.close(), 250);
|
| 19 |
+
}
|
| 20 |
+
});
|
| 21 |
+
</script>
|
| 22 |
+
|
| 23 |
+
<dialog
|
| 24 |
+
class="backdrop:bg-transparent"
|
| 25 |
+
bind:this={dialog}
|
| 26 |
+
onclose={e => {
|
| 27 |
+
e.preventDefault();
|
| 28 |
+
img = undefined;
|
| 29 |
+
}}
|
| 30 |
+
>
|
| 31 |
+
{#if img}
|
| 32 |
+
<!-- Backdrop -->
|
| 33 |
+
<div
|
| 34 |
+
class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/50 backdrop-blur-sm"
|
| 35 |
+
transition:fade={{ duration: 150 }}
|
| 36 |
+
>
|
| 37 |
+
<!-- Content -->
|
| 38 |
+
<img
|
| 39 |
+
class="max-h-[calc(100vh-120px)] max-w-[calc(100vw-120px)] object-contain"
|
| 40 |
+
src={img}
|
| 41 |
+
alt=""
|
| 42 |
+
use:clickOutside={() => (img = undefined)}
|
| 43 |
+
transition:scale={{ start: 0.975, duration: 250 }}
|
| 44 |
+
/>
|
| 45 |
+
|
| 46 |
+
<button
|
| 47 |
+
type="button"
|
| 48 |
+
class="absolute top-3 right-3 inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
|
| 49 |
+
onclick={() => (img = undefined)}
|
| 50 |
+
aria-label="Close modal"
|
| 51 |
+
>
|
| 52 |
+
<div class="text-xl">
|
| 53 |
+
<IconCross />
|
| 54 |
+
</div>
|
| 55 |
+
</button>
|
| 56 |
+
</div>
|
| 57 |
+
{/if}
|
| 58 |
+
</dialog>
|
src/lib/components/inference-playground/message.svelte
CHANGED
|
@@ -6,6 +6,8 @@
|
|
| 6 |
import { FileUpload } from "melt/builders";
|
| 7 |
import { fade } from "svelte/transition";
|
| 8 |
import IconImage from "~icons/carbon/image-reference";
|
|
|
|
|
|
|
| 9 |
|
| 10 |
type Props = {
|
| 11 |
conversation: Conversation;
|
|
@@ -41,6 +43,8 @@
|
|
| 41 |
},
|
| 42 |
disabled: () => !canUploadImgs,
|
| 43 |
});
|
|
|
|
|
|
|
| 44 |
</script>
|
| 45 |
|
| 46 |
<div
|
|
@@ -119,16 +123,28 @@
|
|
| 119 |
</Tooltip>
|
| 120 |
</div>
|
| 121 |
</div>
|
|
|
|
| 122 |
{#if message.images?.length}
|
| 123 |
<div class="mt-2">
|
| 124 |
<div class="flex items-center gap-2">
|
| 125 |
{#each message.images as img (img)}
|
| 126 |
<div class="group/img relative">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
<img src={img} alt="uploaded" class="size-12 rounded-lg object-cover" />
|
| 128 |
<button
|
|
|
|
| 129 |
type="button"
|
| 130 |
-
onclick={
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
| 132 |
>
|
| 133 |
✕
|
| 134 |
</button>
|
|
@@ -138,3 +154,5 @@
|
|
| 138 |
</div>
|
| 139 |
{/if}
|
| 140 |
</div>
|
|
|
|
|
|
|
|
|
| 6 |
import { FileUpload } from "melt/builders";
|
| 7 |
import { fade } from "svelte/transition";
|
| 8 |
import IconImage from "~icons/carbon/image-reference";
|
| 9 |
+
import IconMaximize from "~icons/carbon/maximize";
|
| 10 |
+
import ImgPreview from "./img-preview.svelte";
|
| 11 |
|
| 12 |
type Props = {
|
| 13 |
conversation: Conversation;
|
|
|
|
| 43 |
},
|
| 44 |
disabled: () => !canUploadImgs,
|
| 45 |
});
|
| 46 |
+
|
| 47 |
+
let previewImg = $state<string>();
|
| 48 |
</script>
|
| 49 |
|
| 50 |
<div
|
|
|
|
| 123 |
</Tooltip>
|
| 124 |
</div>
|
| 125 |
</div>
|
| 126 |
+
|
| 127 |
{#if message.images?.length}
|
| 128 |
<div class="mt-2">
|
| 129 |
<div class="flex items-center gap-2">
|
| 130 |
{#each message.images as img (img)}
|
| 131 |
<div class="group/img relative">
|
| 132 |
+
<button
|
| 133 |
+
aria-label="expand"
|
| 134 |
+
class="absolute inset-0 z-10 grid place-items-center bg-gray-800/70 opacity-0 group-hover/img:opacity-100"
|
| 135 |
+
onclick={() => (previewImg = img)}
|
| 136 |
+
>
|
| 137 |
+
<IconMaximize />
|
| 138 |
+
</button>
|
| 139 |
<img src={img} alt="uploaded" class="size-12 rounded-lg object-cover" />
|
| 140 |
<button
|
| 141 |
+
aria-label="remove"
|
| 142 |
type="button"
|
| 143 |
+
onclick={e => {
|
| 144 |
+
e.stopPropagation();
|
| 145 |
+
message.images = message.images?.filter(i => i !== img);
|
| 146 |
+
}}
|
| 147 |
+
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"
|
| 148 |
>
|
| 149 |
✕
|
| 150 |
</button>
|
|
|
|
| 154 |
</div>
|
| 155 |
{/if}
|
| 156 |
</div>
|
| 157 |
+
|
| 158 |
+
<ImgPreview bind:img={previewImg} />
|
src/lib/components/inference-playground/project-select.svelte
CHANGED
|
@@ -8,6 +8,7 @@
|
|
| 8 |
import IconSave from "~icons/carbon/save";
|
| 9 |
import IconDelete from "~icons/carbon/trash-can";
|
| 10 |
import { prompt } from "../prompts.svelte";
|
|
|
|
| 11 |
|
| 12 |
interface Props {
|
| 13 |
class?: string;
|
|
@@ -47,14 +48,29 @@
|
|
| 47 |
<IconCaret />
|
| 48 |
</div>
|
| 49 |
</button>
|
|
|
|
| 50 |
{#if isDefault}
|
| 51 |
-
<
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
{:else}
|
| 55 |
-
<
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
{/if}
|
| 59 |
</div>
|
| 60 |
|
|
|
|
| 8 |
import IconSave from "~icons/carbon/save";
|
| 9 |
import IconDelete from "~icons/carbon/trash-can";
|
| 10 |
import { prompt } from "../prompts.svelte";
|
| 11 |
+
import Tooltip from "../tooltip.svelte";
|
| 12 |
|
| 13 |
interface Props {
|
| 14 |
class?: string;
|
|
|
|
| 48 |
<IconCaret />
|
| 49 |
</div>
|
| 50 |
</button>
|
| 51 |
+
|
| 52 |
{#if isDefault}
|
| 53 |
+
<Tooltip>
|
| 54 |
+
{#snippet trigger(tooltip)}
|
| 55 |
+
<button class="btn size-[32px] p-0" {...tooltip.trigger} onclick={saveProject}>
|
| 56 |
+
<IconSave />
|
| 57 |
+
</button>
|
| 58 |
+
{/snippet}
|
| 59 |
+
Save to Project
|
| 60 |
+
</Tooltip>
|
| 61 |
{:else}
|
| 62 |
+
<Tooltip>
|
| 63 |
+
{#snippet trigger(tooltip)}
|
| 64 |
+
<button
|
| 65 |
+
class="btn size-[32px] p-0"
|
| 66 |
+
{...tooltip.trigger}
|
| 67 |
+
onclick={() => (session.$.activeProjectId = "default")}
|
| 68 |
+
>
|
| 69 |
+
<IconCross />
|
| 70 |
+
</button>
|
| 71 |
+
{/snippet}
|
| 72 |
+
Close project
|
| 73 |
+
</Tooltip>
|
| 74 |
{/if}
|
| 75 |
</div>
|
| 76 |
|
src/lib/components/tooltip.svelte
CHANGED
|
@@ -1,19 +1,27 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
-
import { Tooltip, type TooltipProps } from "melt/builders";
|
| 3 |
import { type ComponentProps, type Extracted } from "melt";
|
|
|
|
| 4 |
import type { Snippet } from "svelte";
|
| 5 |
|
|
|
|
|
|
|
| 6 |
interface Props {
|
| 7 |
children: Snippet;
|
| 8 |
trigger: Snippet<[Tooltip]>;
|
| 9 |
-
placement?: NonNullable<
|
| 10 |
openDelay?: ComponentProps<TooltipProps>["openDelay"];
|
| 11 |
}
|
| 12 |
-
const { children, trigger, placement = "top", openDelay }: Props = $props();
|
| 13 |
|
| 14 |
const tooltip = new Tooltip({
|
| 15 |
forceVisible: true,
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
openDelay: () => openDelay,
|
| 18 |
});
|
| 19 |
</script>
|
|
|
|
| 1 |
<script lang="ts">
|
|
|
|
| 2 |
import { type ComponentProps, type Extracted } from "melt";
|
| 3 |
+
import { Tooltip, type TooltipProps } from "melt/builders";
|
| 4 |
import type { Snippet } from "svelte";
|
| 5 |
|
| 6 |
+
type FloatingConfig = NonNullable<Extracted<TooltipProps["floatingConfig"]>>;
|
| 7 |
+
|
| 8 |
interface Props {
|
| 9 |
children: Snippet;
|
| 10 |
trigger: Snippet<[Tooltip]>;
|
| 11 |
+
placement?: NonNullable<FloatingConfig["computePosition"]>["placement"];
|
| 12 |
openDelay?: ComponentProps<TooltipProps>["openDelay"];
|
| 13 |
}
|
| 14 |
+
const { children, trigger, placement = "top", openDelay = 500 }: Props = $props();
|
| 15 |
|
| 16 |
const tooltip = new Tooltip({
|
| 17 |
forceVisible: true,
|
| 18 |
+
floatingConfig: () => ({
|
| 19 |
+
computePosition: { placement },
|
| 20 |
+
flip: {
|
| 21 |
+
fallbackPlacements: ["bottom"],
|
| 22 |
+
padding: 10,
|
| 23 |
+
},
|
| 24 |
+
}),
|
| 25 |
openDelay: () => openDelay,
|
| 26 |
});
|
| 27 |
</script>
|