Spaces:
Runtime error
Runtime error
machineuser
commited on
Commit
•
76d4920
1
Parent(s):
3b081d5
Sync widgets demo
Browse files- packages/widgets/src/lib/components/Icons/IconHuggingFace.svelte +56 -0
- packages/widgets/src/lib/components/InferenceWidget/InferenceWidget.svelte +3 -0
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetDropzone/WidgetDropzone.svelte +46 -30
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetFileInput/WidgetFileInput.svelte +49 -32
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetFooter/WidgetFooter.svelte +23 -14
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetQuickInput/WidgetQuickInput.svelte +37 -20
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetSubmitBtn/WidgetSubmitBtn.svelte +36 -12
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetTextInput/WidgetTextInput.svelte +28 -13
- packages/widgets/src/lib/components/InferenceWidget/shared/WidgetTextarea/WidgetTextarea.svelte +37 -24
- packages/widgets/src/lib/components/InferenceWidget/shared/helpers.ts +4 -2
- packages/widgets/src/lib/components/InferenceWidget/stores.ts +2 -0
- packages/widgets/src/lib/components/InferenceWidget/widgets/ConversationalWidget/ConversationalWidget.svelte +3 -3
- packages/widgets/src/lib/components/LogInPopover/LogInPopover.svelte +38 -0
- packages/widgets/src/lib/components/Popover/Popover.svelte +148 -0
- packages/widgets/src/lib/utils/ViewUtils.ts +36 -0
- packages/widgets/src/routes/+page.svelte +7 -0
packages/widgets/src/lib/components/Icons/IconHuggingFace.svelte
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9 |
+
aria-hidden="true"
|
10 |
+
focusable="false"
|
11 |
+
role="img"
|
12 |
+
preserveAspectRatio="xMidYMid meet"
|
13 |
+
width="1em"
|
14 |
+
height="1em"
|
15 |
+
viewBox="0 0 95 88"
|
16 |
+
>
|
17 |
+
<path
|
18 |
+
fill="#fff"
|
19 |
+
d="M94.25 70.08a8.28 8.28 0 0 1-.43 6.46 10.57 10.57 0 0 1-3 3.6 25.18 25.18 0 0 1-5.7 3.2 65.74 65.74 0 0 1-7.56 2.65 46.67 46.67 0 0 1-11.42 1.68c-5.42.05-10.09-1.23-13.4-4.5a40.4 40.4 0 0 1-10.14.03c-3.34 3.25-7.99 4.52-13.39 4.47a46.82 46.82 0 0 1-11.43-1.68 66.37 66.37 0 0 1-7.55-2.65c-2.28-.98-4.17-2-5.68-3.2a10.5 10.5 0 0 1-3.02-3.6c-.99-2-1.18-4.3-.42-6.46a8.54 8.54 0 0 1-.33-5.63c.25-.95.66-1.83 1.18-2.61a8.67 8.67 0 0 1 2.1-8.47 8.23 8.23 0 0 1 2.82-2.07 41.75 41.75 0 1 1 81.3-.12 8.27 8.27 0 0 1 3.11 2.19 8.7 8.7 0 0 1 2.1 8.47c.52.78.93 1.66 1.18 2.61a8.61 8.61 0 0 1-.32 5.63Z"
|
20 |
+
/>
|
21 |
+
<path fill="#FFD21E" d="M47.21 76.5a34.75 34.75 0 1 0 0-69.5 34.75 34.75 0 0 0 0 69.5Z" />
|
22 |
+
<path
|
23 |
+
fill="#FF9D0B"
|
24 |
+
d="M81.96 41.75a34.75 34.75 0 1 0-69.5 0 34.75 34.75 0 0 0 69.5 0Zm-73.5 0a38.75 38.75 0 1 1 77.5 0 38.75 38.75 0 0 1-77.5 0Z"
|
25 |
+
/>
|
26 |
+
<path
|
27 |
+
fill="#3A3B45"
|
28 |
+
d="M58.5 32.3c1.28.44 1.78 3.06 3.07 2.38a5 5 0 1 0-6.76-2.07c.61 1.15 2.55-.72 3.7-.32ZM34.95 32.3c-1.28.44-1.79 3.06-3.07 2.38a5 5 0 1 1 6.76-2.07c-.61 1.15-2.56-.72-3.7-.32Z"
|
29 |
+
/>
|
30 |
+
<path
|
31 |
+
fill="#FF323D"
|
32 |
+
d="M46.96 56.29c9.83 0 13-8.76 13-13.26 0-2.34-1.57-1.6-4.09-.36-2.33 1.15-5.46 2.74-8.9 2.74-7.19 0-13-6.88-13-2.38s3.16 13.26 13 13.26Z"
|
33 |
+
/>
|
34 |
+
<path
|
35 |
+
fill="#3A3B45"
|
36 |
+
fill-rule="evenodd"
|
37 |
+
d="M39.43 54a8.7 8.7 0 0 1 5.3-4.49c.4-.12.81.57 1.24 1.28.4.68.82 1.37 1.24 1.37.45 0 .9-.68 1.33-1.35.45-.7.89-1.38 1.32-1.25a8.61 8.61 0 0 1 5 4.17c3.73-2.94 5.1-7.74 5.1-10.7 0-2.34-1.57-1.6-4.09-.36l-.14.07c-2.31 1.15-5.39 2.67-8.77 2.67s-6.45-1.52-8.77-2.67c-2.6-1.29-4.23-2.1-4.23.29 0 3.05 1.46 8.06 5.47 10.97Z"
|
38 |
+
clip-rule="evenodd"
|
39 |
+
/>
|
40 |
+
<path
|
41 |
+
fill="#FF9D0B"
|
42 |
+
d="M70.71 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM24.21 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM17.52 48c-1.62 0-3.06.66-4.07 1.87a5.97 5.97 0 0 0-1.33 3.76 7.1 7.1 0 0 0-1.94-.3c-1.55 0-2.95.59-3.94 1.66a5.8 5.8 0 0 0-.8 7 5.3 5.3 0 0 0-1.79 2.82c-.24.9-.48 2.8.8 4.74a5.22 5.22 0 0 0-.37 5.02c1.02 2.32 3.57 4.14 8.52 6.1 3.07 1.22 5.89 2 5.91 2.01a44.33 44.33 0 0 0 10.93 1.6c5.86 0 10.05-1.8 12.46-5.34 3.88-5.69 3.33-10.9-1.7-15.92-2.77-2.78-4.62-6.87-5-7.77-.78-2.66-2.84-5.62-6.25-5.62a5.7 5.7 0 0 0-4.6 2.46c-1-1.26-1.98-2.25-2.86-2.82A7.4 7.4 0 0 0 17.52 48Zm0 4c.51 0 1.14.22 1.82.65 2.14 1.36 6.25 8.43 7.76 11.18.5.92 1.37 1.31 2.14 1.31 1.55 0 2.75-1.53.15-3.48-3.92-2.93-2.55-7.72-.68-8.01.08-.02.17-.02.24-.02 1.7 0 2.45 2.93 2.45 2.93s2.2 5.52 5.98 9.3c3.77 3.77 3.97 6.8 1.22 10.83-1.88 2.75-5.47 3.58-9.16 3.58-3.81 0-7.73-.9-9.92-1.46-.11-.03-13.45-3.8-11.76-7 .28-.54.75-.76 1.34-.76 2.38 0 6.7 3.54 8.57 3.54.41 0 .7-.17.83-.6.79-2.85-12.06-4.05-10.98-8.17.2-.73.71-1.02 1.44-1.02 3.14 0 10.2 5.53 11.68 5.53.11 0 .2-.03.24-.1.74-1.2.33-2.04-4.9-5.2-5.21-3.16-8.88-5.06-6.8-7.33.24-.26.58-.38 1-.38 3.17 0 10.66 6.82 10.66 6.82s2.02 2.1 3.25 2.1c.28 0 .52-.1.68-.38.86-1.46-8.06-8.22-8.56-11.01-.34-1.9.24-2.85 1.31-2.85Z"
|
43 |
+
/>
|
44 |
+
<path
|
45 |
+
fill="#FFD21E"
|
46 |
+
d="M38.6 76.69c2.75-4.04 2.55-7.07-1.22-10.84-3.78-3.77-5.98-9.3-5.98-9.3s-.82-3.2-2.69-2.9c-1.87.3-3.24 5.08.68 8.01 3.91 2.93-.78 4.92-2.29 2.17-1.5-2.75-5.62-9.82-7.76-11.18-2.13-1.35-3.63-.6-3.13 2.2.5 2.79 9.43 9.55 8.56 11-.87 1.47-3.93-1.71-3.93-1.71s-9.57-8.71-11.66-6.44c-2.08 2.27 1.59 4.17 6.8 7.33 5.23 3.16 5.64 4 4.9 5.2-.75 1.2-12.28-8.53-13.36-4.4-1.08 4.11 11.77 5.3 10.98 8.15-.8 2.85-9.06-5.38-10.74-2.18-1.7 3.21 11.65 6.98 11.76 7.01 4.3 1.12 15.25 3.49 19.08-2.12Z"
|
47 |
+
/>
|
48 |
+
<path
|
49 |
+
fill="#FF9D0B"
|
50 |
+
d="M77.4 48c1.62 0 3.07.66 4.07 1.87a5.97 5.97 0 0 1 1.33 3.76 7.1 7.1 0 0 1 1.95-.3c1.55 0 2.95.59 3.94 1.66a5.8 5.8 0 0 1 .8 7 5.3 5.3 0 0 1 1.78 2.82c.24.9.48 2.8-.8 4.74a5.22 5.22 0 0 1 .37 5.02c-1.02 2.32-3.57 4.14-8.51 6.1-3.08 1.22-5.9 2-5.92 2.01a44.33 44.33 0 0 1-10.93 1.6c-5.86 0-10.05-1.8-12.46-5.34-3.88-5.69-3.33-10.9 1.7-15.92 2.78-2.78 4.63-6.87 5.01-7.77.78-2.66 2.83-5.62 6.24-5.62a5.7 5.7 0 0 1 4.6 2.46c1-1.26 1.98-2.25 2.87-2.82A7.4 7.4 0 0 1 77.4 48Zm0 4c-.51 0-1.13.22-1.82.65-2.13 1.36-6.25 8.43-7.76 11.18a2.43 2.43 0 0 1-2.14 1.31c-1.54 0-2.75-1.53-.14-3.48 3.91-2.93 2.54-7.72.67-8.01a1.54 1.54 0 0 0-.24-.02c-1.7 0-2.45 2.93-2.45 2.93s-2.2 5.52-5.97 9.3c-3.78 3.77-3.98 6.8-1.22 10.83 1.87 2.75 5.47 3.58 9.15 3.58 3.82 0 7.73-.9 9.93-1.46.1-.03 13.45-3.8 11.76-7-.29-.54-.75-.76-1.34-.76-2.38 0-6.71 3.54-8.57 3.54-.42 0-.71-.17-.83-.6-.8-2.85 12.05-4.05 10.97-8.17-.19-.73-.7-1.02-1.44-1.02-3.14 0-10.2 5.53-11.68 5.53-.1 0-.19-.03-.23-.1-.74-1.2-.34-2.04 4.88-5.2 5.23-3.16 8.9-5.06 6.8-7.33-.23-.26-.57-.38-.98-.38-3.18 0-10.67 6.82-10.67 6.82s-2.02 2.1-3.24 2.1a.74.74 0 0 1-.68-.38c-.87-1.46 8.05-8.22 8.55-11.01.34-1.9-.24-2.85-1.31-2.85Z"
|
51 |
+
/>
|
52 |
+
<path
|
53 |
+
fill="#FFD21E"
|
54 |
+
d="M56.33 76.69c-2.75-4.04-2.56-7.07 1.22-10.84 3.77-3.77 5.97-9.3 5.97-9.3s.82-3.2 2.7-2.9c1.86.3 3.23 5.08-.68 8.01-3.92 2.93.78 4.92 2.28 2.17 1.51-2.75 5.63-9.82 7.76-11.18 2.13-1.35 3.64-.6 3.13 2.2-.5 2.79-9.42 9.55-8.55 11 .86 1.47 3.92-1.71 3.92-1.71s9.58-8.71 11.66-6.44c2.08 2.27-1.58 4.17-6.8 7.33-5.23 3.16-5.63 4-4.9 5.2.75 1.2 12.28-8.53 13.36-4.4 1.08 4.11-11.76 5.3-10.97 8.15.8 2.85 9.05-5.38 10.74-2.18 1.69 3.21-11.65 6.98-11.76 7.01-4.31 1.12-15.26 3.49-19.08-2.12Z"
|
55 |
+
/>
|
56 |
+
</svg>
|
packages/widgets/src/lib/components/InferenceWidget/InferenceWidget.svelte
CHANGED
@@ -28,6 +28,7 @@
|
|
28 |
import ZeroShotImageClassificationWidget from "./widgets/ZeroShotImageClassificationWidget/ZeroShotImageClassificationWidget.svelte";
|
29 |
import type { WidgetType } from "@huggingface/tasks";
|
30 |
import WidgetInfo from "./shared/WidgetInfo/WidgetInfo.svelte";
|
|
|
31 |
|
32 |
export let apiToken: WidgetProps["apiToken"] = undefined;
|
33 |
export let callApiOnMount = false;
|
@@ -85,6 +86,8 @@
|
|
85 |
? WIDGET_COMPONENTS[model.pipeline_tag as keyof typeof WIDGET_COMPONENTS]
|
86 |
: undefined;
|
87 |
|
|
|
|
|
88 |
// prettier-ignore
|
89 |
$: widgetProps = ({
|
90 |
apiToken,
|
|
|
28 |
import ZeroShotImageClassificationWidget from "./widgets/ZeroShotImageClassificationWidget/ZeroShotImageClassificationWidget.svelte";
|
29 |
import type { WidgetType } from "@huggingface/tasks";
|
30 |
import WidgetInfo from "./shared/WidgetInfo/WidgetInfo.svelte";
|
31 |
+
import { isLoggedIn as isLoggedInStore } from "./stores.js";
|
32 |
|
33 |
export let apiToken: WidgetProps["apiToken"] = undefined;
|
34 |
export let callApiOnMount = false;
|
|
|
86 |
? WIDGET_COMPONENTS[model.pipeline_tag as keyof typeof WIDGET_COMPONENTS]
|
87 |
: undefined;
|
88 |
|
89 |
+
$isLoggedInStore = isLoggedIn;
|
90 |
+
|
91 |
// prettier-ignore
|
92 |
$: widgetProps = ({
|
93 |
apiToken,
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetDropzone/WidgetDropzone.svelte
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
<script lang="ts">
|
|
|
2 |
import IconSpin from "../../../Icons/IconSpin.svelte";
|
3 |
import { getBlobFromUrl } from "../../shared/helpers.js";
|
|
|
4 |
|
5 |
export let accept = "image/*";
|
6 |
export let classNames = "";
|
@@ -13,6 +15,7 @@
|
|
13 |
|
14 |
let fileInput: HTMLInputElement;
|
15 |
let isDragging = false;
|
|
|
16 |
|
17 |
function onChange() {
|
18 |
const file = fileInput.files?.[0];
|
@@ -23,6 +26,12 @@
|
|
23 |
|
24 |
async function onDrop(e: DragEvent) {
|
25 |
isDragging = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
const itemList = e.dataTransfer?.items;
|
27 |
if (!itemList || isLoading) {
|
28 |
return;
|
@@ -54,36 +63,43 @@
|
|
54 |
style="display: none;"
|
55 |
type="file"
|
56 |
/>
|
57 |
-
|
58 |
-
<
|
59 |
-
|
|
|
|
|
60 |
{isDisabled ? 'pointer-events-none' : ''}
|
61 |
{isDragging ? 'border-green-300 bg-green-50 text-green-500' : 'text-gray-500'}
|
62 |
{classNames}"
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<script lang="ts">
|
2 |
+
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
|
3 |
import IconSpin from "../../../Icons/IconSpin.svelte";
|
4 |
import { getBlobFromUrl } from "../../shared/helpers.js";
|
5 |
+
import { isLoggedIn } from "../../stores.js";
|
6 |
|
7 |
export let accept = "image/*";
|
8 |
export let classNames = "";
|
|
|
15 |
|
16 |
let fileInput: HTMLInputElement;
|
17 |
let isDragging = false;
|
18 |
+
let popOverOpen = false;
|
19 |
|
20 |
function onChange() {
|
21 |
const file = fileInput.files?.[0];
|
|
|
26 |
|
27 |
async function onDrop(e: DragEvent) {
|
28 |
isDragging = false;
|
29 |
+
|
30 |
+
if (!$isLoggedIn) {
|
31 |
+
popOverOpen = true;
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
|
35 |
const itemList = e.dataTransfer?.items;
|
36 |
if (!itemList || isLoading) {
|
37 |
return;
|
|
|
63 |
style="display: none;"
|
64 |
type="file"
|
65 |
/>
|
66 |
+
|
67 |
+
<LogInPopover bind:open={popOverOpen}>
|
68 |
+
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
69 |
+
<div
|
70 |
+
class="relative cursor-pointer rounded border-2 border-dashed px-3 py-7 text-center
|
71 |
{isDisabled ? 'pointer-events-none' : ''}
|
72 |
{isDragging ? 'border-green-300 bg-green-50 text-green-500' : 'text-gray-500'}
|
73 |
{classNames}"
|
74 |
+
on:click={() => {
|
75 |
+
if (!$isLoggedIn) {
|
76 |
+
popOverOpen = true;
|
77 |
+
return;
|
78 |
+
}
|
79 |
+
fileInput.click();
|
80 |
+
}}
|
81 |
+
on:dragenter={() => {
|
82 |
+
isDragging = true;
|
83 |
+
}}
|
84 |
+
on:dragleave={() => {
|
85 |
+
isDragging = false;
|
86 |
+
}}
|
87 |
+
on:dragover|preventDefault
|
88 |
+
on:drop|preventDefault={onDrop}
|
89 |
+
>
|
90 |
+
{#if !imgSrc && !isDisabled}
|
91 |
+
<span class="pointer-events-none text-sm">{label}</span>
|
92 |
+
{:else}
|
93 |
+
<div class={isDragging ? "pointer-events-none" : ""}>
|
94 |
+
<slot />
|
95 |
+
</div>
|
96 |
+
{/if}
|
97 |
+
{#if isLoading}
|
98 |
+
<div
|
99 |
+
class="absolute top-1/2 left-1/2 flex h-12 w-12 -translate-x-1/2 -translate-y-1/2 transform items-center justify-center rounded-full border border-gray-100 bg-white shadow"
|
100 |
+
>
|
101 |
+
<IconSpin classNames="text-purple-500 animate-spin h-6 w-6" />
|
102 |
+
</div>
|
103 |
+
{/if}
|
104 |
+
</div>
|
105 |
+
</LogInPopover>
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetFileInput/WidgetFileInput.svelte
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
<script lang="ts">
|
2 |
import IconSpin from "../../../Icons/IconSpin.svelte";
|
3 |
import IconFile from "../../../Icons/IconFile.svelte";
|
|
|
|
|
4 |
|
5 |
export let accept: string | undefined;
|
6 |
export let classNames = "";
|
@@ -11,8 +13,14 @@
|
|
11 |
|
12 |
let fileInput: HTMLInputElement;
|
13 |
let isDragging = false;
|
|
|
14 |
|
15 |
function onChange() {
|
|
|
|
|
|
|
|
|
|
|
16 |
const file = fileInput.files?.[0];
|
17 |
if (file) {
|
18 |
onSelectFile(file);
|
@@ -21,36 +29,45 @@
|
|
21 |
</script>
|
22 |
|
23 |
{#if !isDisabled}
|
24 |
-
<
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
{/if}
|
|
|
1 |
<script lang="ts">
|
2 |
import IconSpin from "../../../Icons/IconSpin.svelte";
|
3 |
import IconFile from "../../../Icons/IconFile.svelte";
|
4 |
+
import { isLoggedIn } from "../../stores.js";
|
5 |
+
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
|
6 |
|
7 |
export let accept: string | undefined;
|
8 |
export let classNames = "";
|
|
|
13 |
|
14 |
let fileInput: HTMLInputElement;
|
15 |
let isDragging = false;
|
16 |
+
let popOverOpen = false;
|
17 |
|
18 |
function onChange() {
|
19 |
+
if (!$isLoggedIn) {
|
20 |
+
popOverOpen = true;
|
21 |
+
return;
|
22 |
+
}
|
23 |
+
|
24 |
const file = fileInput.files?.[0];
|
25 |
if (file) {
|
26 |
onSelectFile(file);
|
|
|
29 |
</script>
|
30 |
|
31 |
{#if !isDisabled}
|
32 |
+
<LogInPopover bind:open={popOverOpen}>
|
33 |
+
<button
|
34 |
+
class={classNames}
|
35 |
+
on:click={(e) => {
|
36 |
+
if (!$isLoggedIn) {
|
37 |
+
popOverOpen = true;
|
38 |
+
e.preventDefault();
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
}}
|
42 |
+
on:dragenter={() => {
|
43 |
+
isDragging = true;
|
44 |
+
}}
|
45 |
+
on:dragover|preventDefault
|
46 |
+
on:dragleave={() => {
|
47 |
+
isDragging = false;
|
48 |
+
}}
|
49 |
+
on:drop|preventDefault={(e) => {
|
50 |
+
isDragging = false;
|
51 |
+
fileInput.files = e.dataTransfer?.files ?? null;
|
52 |
+
onChange();
|
53 |
+
}}
|
54 |
+
>
|
55 |
+
<label class="btn-widget {isDragging ? 'ring' : ''} {isLoading ? 'text-gray-600' : ''}">
|
56 |
+
{#if isLoading}
|
57 |
+
<IconSpin classNames="-ml-1 mr-1.5 text-gray-600 animate-spin" />
|
58 |
+
{:else}
|
59 |
+
<IconFile classNames="-ml-1 mr-1.5" />
|
60 |
+
{/if}
|
61 |
+
<input
|
62 |
+
{accept}
|
63 |
+
bind:this={fileInput}
|
64 |
+
on:change={onChange}
|
65 |
+
disabled={isLoading || isDisabled}
|
66 |
+
style="display: none;"
|
67 |
+
type="file"
|
68 |
+
/>
|
69 |
+
{label}
|
70 |
+
</label>
|
71 |
+
</button>
|
72 |
+
</LogInPopover>
|
73 |
{/if}
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetFooter/WidgetFooter.svelte
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
<script lang="ts">
|
2 |
import type { WidgetProps } from "../types.js";
|
3 |
-
import {
|
4 |
-
import { widgetStates, updateWidgetState } from "../../stores.js";
|
5 |
import IconCode from "../../..//Icons/IconCode.svelte";
|
6 |
import IconMaximize from "../../..//Icons/IconMaximize.svelte";
|
|
|
7 |
|
8 |
export let model: WidgetProps["model"];
|
9 |
export let outputJson: string;
|
@@ -12,6 +12,7 @@
|
|
12 |
$: isMaximized = $widgetStates?.[model.id]?.isMaximized;
|
13 |
|
14 |
let isOutputJsonVisible = false;
|
|
|
15 |
</script>
|
16 |
|
17 |
<div class="mt-auto flex items-center pt-4 text-xs text-gray-500">
|
@@ -28,18 +29,26 @@
|
|
28 |
JSON Output
|
29 |
</button>
|
30 |
{/if}
|
31 |
-
<
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
</div>
|
44 |
{#if outputJson && isOutputJsonVisible}
|
45 |
<pre
|
|
|
1 |
<script lang="ts">
|
2 |
import type { WidgetProps } from "../types.js";
|
3 |
+
import { widgetStates, updateWidgetState, isLoggedIn } from "../../stores.js";
|
|
|
4 |
import IconCode from "../../..//Icons/IconCode.svelte";
|
5 |
import IconMaximize from "../../..//Icons/IconMaximize.svelte";
|
6 |
+
import LogInPopover from "../../../../components/LogInPopover/LogInPopover.svelte";
|
7 |
|
8 |
export let model: WidgetProps["model"];
|
9 |
export let outputJson: string;
|
|
|
12 |
$: isMaximized = $widgetStates?.[model.id]?.isMaximized;
|
13 |
|
14 |
let isOutputJsonVisible = false;
|
15 |
+
let popOverOpen = false;
|
16 |
</script>
|
17 |
|
18 |
<div class="mt-auto flex items-center pt-4 text-xs text-gray-500">
|
|
|
29 |
JSON Output
|
30 |
</button>
|
31 |
{/if}
|
32 |
+
<LogInPopover bind:open={popOverOpen} classNames="ml-auto">
|
33 |
+
<button
|
34 |
+
class="flex items-center"
|
35 |
+
type="button"
|
36 |
+
on:click|preventDefault={() => {
|
37 |
+
if (!$isLoggedIn) {
|
38 |
+
popOverOpen = true;
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
updateWidgetState(model.id, "isMaximized", !isMaximized);
|
42 |
+
}}
|
43 |
+
>
|
44 |
+
<IconMaximize classNames="mr-1" />
|
45 |
+
{#if !isMaximized}
|
46 |
+
Maximize
|
47 |
+
{:else}
|
48 |
+
Minimize
|
49 |
+
{/if}
|
50 |
+
</button>
|
51 |
+
</LogInPopover>
|
52 |
</div>
|
53 |
{#if outputJson && isOutputJsonVisible}
|
54 |
<pre
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetQuickInput/WidgetQuickInput.svelte
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
<script lang="ts">
|
|
|
2 |
import { onCmdEnter } from "../../../../utils/ViewUtils.js";
|
3 |
import WidgetSubmitBtn from "../WidgetSubmitBtn/WidgetSubmitBtn.svelte";
|
|
|
|
|
4 |
|
5 |
export let flatTop = false;
|
6 |
export let isLoading: boolean;
|
@@ -9,25 +12,39 @@
|
|
9 |
export let placeholder = "Your sentence here...";
|
10 |
export let submitButtonLabel: string | undefined = undefined;
|
11 |
export let value: string = "";
|
|
|
|
|
|
|
|
|
12 |
</script>
|
13 |
|
14 |
-
<
|
15 |
-
<
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { createEventDispatcher } from "svelte";
|
3 |
import { onCmdEnter } from "../../../../utils/ViewUtils.js";
|
4 |
import WidgetSubmitBtn from "../WidgetSubmitBtn/WidgetSubmitBtn.svelte";
|
5 |
+
import { isLoggedIn } from "../../stores.js";
|
6 |
+
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
|
7 |
|
8 |
export let flatTop = false;
|
9 |
export let isLoading: boolean;
|
|
|
12 |
export let placeholder = "Your sentence here...";
|
13 |
export let submitButtonLabel: string | undefined = undefined;
|
14 |
export let value: string = "";
|
15 |
+
|
16 |
+
let popOverOpen = false;
|
17 |
+
|
18 |
+
const dispatch = createEventDispatcher<{ cmdEnter: void }>();
|
19 |
</script>
|
20 |
|
21 |
+
<LogInPopover bind:open={popOverOpen}>
|
22 |
+
<div class="flex h-10">
|
23 |
+
<input
|
24 |
+
bind:value
|
25 |
+
class="form-input-alt min-w-0 flex-1 rounded-r-none {flatTop ? 'rounded-t-none' : ''}"
|
26 |
+
placeholder={isDisabled ? "" : placeholder}
|
27 |
+
required={true}
|
28 |
+
type="text"
|
29 |
+
disabled={isLoading || isDisabled}
|
30 |
+
autocomplete="off"
|
31 |
+
use:onCmdEnter={{ disabled: isLoading || isDisabled }}
|
32 |
+
on:cmdEnter={() => {
|
33 |
+
if (!$isLoggedIn) {
|
34 |
+
popOverOpen = true;
|
35 |
+
return;
|
36 |
+
}
|
37 |
+
dispatch("cmdEnter");
|
38 |
+
}}
|
39 |
+
/>
|
40 |
+
<WidgetSubmitBtn
|
41 |
+
classNames="rounded-l-none border-l-0 {flatTop ? 'rounded-t-none' : ''}"
|
42 |
+
{isLoading}
|
43 |
+
{isDisabled}
|
44 |
+
label={submitButtonLabel}
|
45 |
+
onClick={onClickSubmitBtn}
|
46 |
+
withParentLoginPopover={true}
|
47 |
+
on:logInPopover={() => (popOverOpen = true)}
|
48 |
+
/>
|
49 |
+
</div>
|
50 |
+
</LogInPopover>
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetSubmitBtn/WidgetSubmitBtn.svelte
CHANGED
@@ -1,24 +1,48 @@
|
|
1 |
<script lang="ts">
|
|
|
2 |
import IconSpin from "../../..//Icons/IconSpin.svelte";
|
|
|
|
|
3 |
|
4 |
export let classNames = "";
|
5 |
export let isDisabled = false;
|
6 |
export let isLoading: boolean;
|
7 |
export let label = "Compute";
|
8 |
export let onClick: () => void;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
</script>
|
10 |
|
11 |
{#if !isDisabled}
|
12 |
-
<
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
{
|
22 |
-
|
23 |
-
|
|
|
|
|
24 |
{/if}
|
|
|
1 |
<script lang="ts">
|
2 |
+
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
|
3 |
import IconSpin from "../../..//Icons/IconSpin.svelte";
|
4 |
+
import { isLoggedIn } from "../../stores.js";
|
5 |
+
import { createEventDispatcher } from "svelte";
|
6 |
|
7 |
export let classNames = "";
|
8 |
export let isDisabled = false;
|
9 |
export let isLoading: boolean;
|
10 |
export let label = "Compute";
|
11 |
export let onClick: () => void;
|
12 |
+
export let withParentLoginPopover = false;
|
13 |
+
|
14 |
+
let popOverOpen = false;
|
15 |
+
|
16 |
+
const dispatch = createEventDispatcher<{ logInPopover: void }>();
|
17 |
+
|
18 |
+
function _onClick() {
|
19 |
+
if (!$isLoggedIn) {
|
20 |
+
if (withParentLoginPopover) {
|
21 |
+
// tell parent element to show log in pop over
|
22 |
+
dispatch("logInPopover");
|
23 |
+
} else {
|
24 |
+
popOverOpen = true;
|
25 |
+
}
|
26 |
+
return;
|
27 |
+
}
|
28 |
+
|
29 |
+
onClick();
|
30 |
+
}
|
31 |
</script>
|
32 |
|
33 |
{#if !isDisabled}
|
34 |
+
<LogInPopover bind:open={popOverOpen}>
|
35 |
+
<button
|
36 |
+
class="btn-widget h-10 w-24 px-5 {classNames}"
|
37 |
+
disabled={isDisabled || isLoading}
|
38 |
+
on:click|preventDefault={_onClick}
|
39 |
+
type="submit"
|
40 |
+
>
|
41 |
+
{#if isLoading}
|
42 |
+
<IconSpin classNames="text-gray-600 animate-spin" />
|
43 |
+
{:else}
|
44 |
+
{label}
|
45 |
+
{/if}
|
46 |
+
</button>
|
47 |
+
</LogInPopover>
|
48 |
{/if}
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetTextInput/WidgetTextInput.svelte
CHANGED
@@ -1,24 +1,39 @@
|
|
1 |
<script lang="ts">
|
|
|
|
|
2 |
import { onCmdEnter } from "../../../../utils/ViewUtils.js";
|
3 |
import WidgetLabel from "../WidgetLabel/WidgetLabel.svelte";
|
|
|
4 |
|
5 |
export let label: string | undefined = undefined;
|
6 |
export let placeholder: string = "Your sentence here...";
|
7 |
export let isLoading = false;
|
8 |
export let isDisabled = false;
|
9 |
export let value: string;
|
|
|
|
|
|
|
|
|
10 |
</script>
|
11 |
|
12 |
-
<
|
13 |
-
<
|
14 |
-
<
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<script lang="ts">
|
2 |
+
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
|
3 |
+
import { createEventDispatcher } from "svelte";
|
4 |
import { onCmdEnter } from "../../../../utils/ViewUtils.js";
|
5 |
import WidgetLabel from "../WidgetLabel/WidgetLabel.svelte";
|
6 |
+
import { isLoggedIn } from "../../stores.js";
|
7 |
|
8 |
export let label: string | undefined = undefined;
|
9 |
export let placeholder: string = "Your sentence here...";
|
10 |
export let isLoading = false;
|
11 |
export let isDisabled = false;
|
12 |
export let value: string;
|
13 |
+
|
14 |
+
let popOverOpen = false;
|
15 |
+
|
16 |
+
const dispatch = createEventDispatcher<{ cmdEnter: void }>();
|
17 |
</script>
|
18 |
|
19 |
+
<LogInPopover bind:open={popOverOpen}>
|
20 |
+
<WidgetLabel {label}>
|
21 |
+
<svelte:fragment slot="after">
|
22 |
+
<input
|
23 |
+
bind:value
|
24 |
+
class="{label ? 'mt-1.5' : ''} form-input-alt block w-full"
|
25 |
+
placeholder={isDisabled ? "" : placeholder}
|
26 |
+
disabled={isDisabled}
|
27 |
+
type="text"
|
28 |
+
use:onCmdEnter={{ disabled: isLoading || isDisabled }}
|
29 |
+
on:cmdEnter={() => {
|
30 |
+
if (!$isLoggedIn) {
|
31 |
+
popOverOpen = true;
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
dispatch("cmdEnter");
|
35 |
+
}}
|
36 |
+
/>
|
37 |
+
</svelte:fragment>
|
38 |
+
</WidgetLabel>
|
39 |
+
</LogInPopover>
|
packages/widgets/src/lib/components/InferenceWidget/shared/WidgetTextarea/WidgetTextarea.svelte
CHANGED
@@ -1,8 +1,10 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { tick } from "svelte";
|
3 |
|
4 |
import { delay, onCmdEnter } from "../../../../utils/ViewUtils.js";
|
5 |
import WidgetLabel from "../WidgetLabel/WidgetLabel.svelte";
|
|
|
|
|
6 |
|
7 |
export let label: string = "";
|
8 |
export let placeholder: string = "Your sentence here...";
|
@@ -13,10 +15,13 @@
|
|
13 |
|
14 |
let containerSpanEl: HTMLSpanElement;
|
15 |
let isOnFocus = false;
|
|
|
16 |
const typingEffectSpeedMs = 12;
|
17 |
const classNamesInput = "whitespace-pre-wrap inline font-normal text-black dark:text-white";
|
18 |
const classNamesOutput = "whitespace-pre-wrap inline text-blue-600 dark:text-blue-400";
|
19 |
|
|
|
|
|
20 |
export async function renderTextOutput(outputTxt: string, typingEffect = true): Promise<void> {
|
21 |
const spanEl = document.createElement("span");
|
22 |
spanEl.contentEditable = isDisabled ? "false" : "true";
|
@@ -90,29 +95,37 @@
|
|
90 |
}
|
91 |
</script>
|
92 |
|
93 |
-
<
|
94 |
-
<
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
? '
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
<style>
|
118 |
span[contenteditable]:empty::before {
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { createEventDispatcher, tick } from "svelte";
|
3 |
|
4 |
import { delay, onCmdEnter } from "../../../../utils/ViewUtils.js";
|
5 |
import WidgetLabel from "../WidgetLabel/WidgetLabel.svelte";
|
6 |
+
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
|
7 |
+
import { isLoggedIn } from "../../stores.js";
|
8 |
|
9 |
export let label: string = "";
|
10 |
export let placeholder: string = "Your sentence here...";
|
|
|
15 |
|
16 |
let containerSpanEl: HTMLSpanElement;
|
17 |
let isOnFocus = false;
|
18 |
+
let popOverOpen = false;
|
19 |
const typingEffectSpeedMs = 12;
|
20 |
const classNamesInput = "whitespace-pre-wrap inline font-normal text-black dark:text-white";
|
21 |
const classNamesOutput = "whitespace-pre-wrap inline text-blue-600 dark:text-blue-400";
|
22 |
|
23 |
+
const dispatch = createEventDispatcher<{ cmdEnter: void }>();
|
24 |
+
|
25 |
export async function renderTextOutput(outputTxt: string, typingEffect = true): Promise<void> {
|
26 |
const spanEl = document.createElement("span");
|
27 |
spanEl.contentEditable = isDisabled ? "false" : "true";
|
|
|
95 |
}
|
96 |
</script>
|
97 |
|
98 |
+
<LogInPopover bind:open={popOverOpen}>
|
99 |
+
<WidgetLabel {label}>
|
100 |
+
<svelte:fragment slot="after">
|
101 |
+
<!-- `whitespace-pre-wrap inline-block` are needed to get correct newlines from `el.textContent` on Chrome -->
|
102 |
+
<span
|
103 |
+
class="{label ? 'mt-1.5' : ''} block w-full resize-y overflow-auto py-2 px-3 {size === 'small'
|
104 |
+
? 'min-h-[42px]'
|
105 |
+
: 'min-h-[144px]'} inline-block max-h-[500px] whitespace-pre-wrap rounded-lg border border-gray-200 shadow-inner outline-none focus:shadow-inner focus:ring focus:ring-blue-200 dark:bg-gray-925"
|
106 |
+
role="textbox"
|
107 |
+
style="--placeholder: '{isDisabled ? '' : placeholder}'"
|
108 |
+
spellcheck="false"
|
109 |
+
dir="auto"
|
110 |
+
contenteditable
|
111 |
+
class:pointer-events-none={isLoading || isDisabled}
|
112 |
+
use:onCmdEnter={{ disabled: isLoading || isDisabled }}
|
113 |
+
on:cmdEnter={() => {
|
114 |
+
if (!$isLoggedIn) {
|
115 |
+
popOverOpen = true;
|
116 |
+
return;
|
117 |
+
}
|
118 |
+
dispatch("cmdEnter");
|
119 |
+
}}
|
120 |
+
bind:this={containerSpanEl}
|
121 |
+
on:paste|preventDefault={handlePaste}
|
122 |
+
on:input={updateInnerTextValue}
|
123 |
+
on:focus={onFocus}
|
124 |
+
on:blur={() => (isOnFocus = false)}
|
125 |
+
/>
|
126 |
+
</svelte:fragment>
|
127 |
+
</WidgetLabel>
|
128 |
+
</LogInPopover>
|
129 |
|
130 |
<style>
|
131 |
span[contenteditable]:empty::before {
|
packages/widgets/src/lib/components/InferenceWidget/shared/helpers.ts
CHANGED
@@ -2,6 +2,8 @@ import type { ModelData, WidgetExampleAttribute } from "@huggingface/tasks";
|
|
2 |
import { parseJSON } from "../../../utils/ViewUtils.js";
|
3 |
import { ComputeType, type ModelLoadInfo, type TableData } from "./types.js";
|
4 |
import { LoadState } from "./types.js";
|
|
|
|
|
5 |
|
6 |
const KEYS_TEXT: WidgetExampleAttribute[] = ["text", "context", "candidate_labels"];
|
7 |
const KEYS_TABLE: WidgetExampleAttribute[] = ["table", "structured_data"];
|
@@ -102,10 +104,10 @@ export async function callInferenceApi<T>(
|
|
102 |
if (waitForModel) {
|
103 |
headers.set("X-Wait-For-Model", "true");
|
104 |
}
|
105 |
-
if (useCache === false) {
|
106 |
headers.set("X-Use-Cache", "false");
|
107 |
}
|
108 |
-
if (isOnLoadCall) {
|
109 |
headers.set("X-Load-Model", "0");
|
110 |
}
|
111 |
|
|
|
2 |
import { parseJSON } from "../../../utils/ViewUtils.js";
|
3 |
import { ComputeType, type ModelLoadInfo, type TableData } from "./types.js";
|
4 |
import { LoadState } from "./types.js";
|
5 |
+
import { isLoggedIn } from "../stores.js";
|
6 |
+
import { get } from "svelte/store";
|
7 |
|
8 |
const KEYS_TEXT: WidgetExampleAttribute[] = ["text", "context", "candidate_labels"];
|
9 |
const KEYS_TABLE: WidgetExampleAttribute[] = ["table", "structured_data"];
|
|
|
104 |
if (waitForModel) {
|
105 |
headers.set("X-Wait-For-Model", "true");
|
106 |
}
|
107 |
+
if (useCache === false && get(isLoggedIn)) {
|
108 |
headers.set("X-Use-Cache", "false");
|
109 |
}
|
110 |
+
if (isOnLoadCall || !get(isLoggedIn)) {
|
111 |
headers.set("X-Load-Model", "0");
|
112 |
}
|
113 |
|
packages/widgets/src/lib/components/InferenceWidget/stores.ts
CHANGED
@@ -6,6 +6,8 @@ export const modelLoadStates = writable<Record<ModelData["id"], ModelLoadInfo>>(
|
|
6 |
|
7 |
export const widgetNoInference = writable<Record<ModelData["id"], boolean>>({});
|
8 |
|
|
|
|
|
9 |
export const widgetStates = writable<Record<ModelData["id"], WidgetState>>({});
|
10 |
|
11 |
const tgiSupportedModels = writable<Set<string> | undefined>(undefined);
|
|
|
6 |
|
7 |
export const widgetNoInference = writable<Record<ModelData["id"], boolean>>({});
|
8 |
|
9 |
+
export const isLoggedIn = writable<boolean>(false);
|
10 |
+
|
11 |
export const widgetStates = writable<Record<ModelData["id"], WidgetState>>({});
|
12 |
|
13 |
const tgiSupportedModels = writable<Set<string> | undefined>(undefined);
|
packages/widgets/src/lib/components/InferenceWidget/widgets/ConversationalWidget/ConversationalWidget.svelte
CHANGED
@@ -21,7 +21,7 @@
|
|
21 |
import WidgetQuickInput from "../../shared/WidgetQuickInput/WidgetQuickInput.svelte";
|
22 |
import WidgetWrapper from "../../shared/WidgetWrapper/WidgetWrapper.svelte";
|
23 |
import { addInferenceParameters, updateUrl } from "../../shared/helpers.js";
|
24 |
-
import { widgetStates, getTgiSupportedModels } from "../../stores.js";
|
25 |
import type { Writable } from "svelte/store";
|
26 |
import { isChatInput, isTextInput } from "../../shared/inputValidation.js";
|
27 |
import { isValidOutputText } from "../../shared/outputValidation.js";
|
@@ -152,10 +152,10 @@
|
|
152 |
error = "";
|
153 |
try {
|
154 |
const opts = {
|
155 |
-
dont_load_model: isOnLoadCall,
|
156 |
includeCredentials,
|
157 |
signal: abort?.signal,
|
158 |
-
use_cache: useCache,
|
159 |
wait_for_model: withModelLoading,
|
160 |
} satisfies Options;
|
161 |
|
|
|
21 |
import WidgetQuickInput from "../../shared/WidgetQuickInput/WidgetQuickInput.svelte";
|
22 |
import WidgetWrapper from "../../shared/WidgetWrapper/WidgetWrapper.svelte";
|
23 |
import { addInferenceParameters, updateUrl } from "../../shared/helpers.js";
|
24 |
+
import { widgetStates, getTgiSupportedModels, isLoggedIn } from "../../stores.js";
|
25 |
import type { Writable } from "svelte/store";
|
26 |
import { isChatInput, isTextInput } from "../../shared/inputValidation.js";
|
27 |
import { isValidOutputText } from "../../shared/outputValidation.js";
|
|
|
152 |
error = "";
|
153 |
try {
|
154 |
const opts = {
|
155 |
+
dont_load_model: isOnLoadCall || !$isLoggedIn,
|
156 |
includeCredentials,
|
157 |
signal: abort?.signal,
|
158 |
+
use_cache: useCache || !$isLoggedIn,
|
159 |
wait_for_model: withModelLoading,
|
160 |
} satisfies Options;
|
161 |
|
packages/widgets/src/lib/components/LogInPopover/LogInPopover.svelte
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Popover from "../Popover/Popover.svelte";
|
3 |
+
import IconHuggingFace from "../Icons/IconHuggingFace.svelte";
|
4 |
+
import { isLoggedIn } from "../InferenceWidget/stores.js";
|
5 |
+
|
6 |
+
export let open = false;
|
7 |
+
export let classNames = "";
|
8 |
+
|
9 |
+
let anchorElement: HTMLElement;
|
10 |
+
</script>
|
11 |
+
|
12 |
+
<div bind:this={anchorElement} class={classNames}>
|
13 |
+
<slot />
|
14 |
+
</div>
|
15 |
+
|
16 |
+
{#if open && !$isLoggedIn}
|
17 |
+
<Popover classNames="w-80" {anchorElement} on:close={() => (open = false)}>
|
18 |
+
<div class="flex items-center gap-x-3 text-sm leading-tight">
|
19 |
+
<IconHuggingFace classNames="text-5xl" />
|
20 |
+
Please login with your Hugging Face account to run the widgets.
|
21 |
+
</div>
|
22 |
+
<div class="flex text-sm items-center gap-x-2.5 mt-2">
|
23 |
+
<a
|
24 |
+
href="https://huggingface.co/login{typeof window !== 'undefined'
|
25 |
+
? `?next=${encodeURIComponent(window.location.href)}`
|
26 |
+
: ''}"
|
27 |
+
class="bg-black text-white px-3 py-1 rounded-full dark:!bg-gray-100 dark:!text-black">Log In</a
|
28 |
+
>
|
29 |
+
<span class="text-gray-400">or</span>
|
30 |
+
<a
|
31 |
+
href="https://huggingface.co/join{typeof window !== 'undefined'
|
32 |
+
? `?next=${encodeURIComponent(window.location.href)}`
|
33 |
+
: ''}"
|
34 |
+
class="py-1 rounded-full underline decoration-gray-400 underline-offset-2">Create a free account</a
|
35 |
+
>
|
36 |
+
</div>
|
37 |
+
</Popover>
|
38 |
+
{/if}
|
packages/widgets/src/lib/components/Popover/Popover.svelte
ADDED
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount, tick, createEventDispatcher } from "svelte";
|
3 |
+
import { fade } from "svelte/transition";
|
4 |
+
import { debounce, portalToBody } from "../../utils/ViewUtils.js";
|
5 |
+
|
6 |
+
export let classNames = "";
|
7 |
+
export let anchorElement: HTMLElement;
|
8 |
+
export let alignment: "start" | "center" | "end" | "auto" = "auto";
|
9 |
+
export let placement: "top" | "bottom" | "auto" | "prefer-top" | "prefer-bottom" = "auto";
|
10 |
+
export let waitForContent = false;
|
11 |
+
export let size: "sm" | "md" = "md";
|
12 |
+
export let invertedColors = false;
|
13 |
+
export let touchOnly = false;
|
14 |
+
|
15 |
+
let popoverElement: HTMLDivElement;
|
16 |
+
|
17 |
+
/// sizes of the arrow and its padding, needed to position the popover position correctly
|
18 |
+
const ARROW_PADDING = 24;
|
19 |
+
const ARROW_SIZE = 10;
|
20 |
+
|
21 |
+
/// to prevent the toast from being too close to the edge of the screen
|
22 |
+
const HIT_ZONE_MARGIN = 80;
|
23 |
+
|
24 |
+
const dispatch = createEventDispatcher<{ close: void }>();
|
25 |
+
|
26 |
+
let computedAlignment = alignment === "auto" ? "center" : alignment;
|
27 |
+
let computedPlacement = placement === "auto" ? "bottom" : placement;
|
28 |
+
|
29 |
+
let left: number;
|
30 |
+
let top: number;
|
31 |
+
let width: number;
|
32 |
+
let height: number;
|
33 |
+
|
34 |
+
let popoverShift: number;
|
35 |
+
let isTouchOnly = false;
|
36 |
+
let isActive = true;
|
37 |
+
|
38 |
+
function updatePlacement(anchorBbox: DOMRect, pageHeight: number) {
|
39 |
+
if (pageHeight > 0) {
|
40 |
+
if (placement === "auto") {
|
41 |
+
/// check if the anchor is closer to the top or bottom of the page
|
42 |
+
computedPlacement = anchorBbox.top > pageHeight / 2 ? "top" : "bottom";
|
43 |
+
} else if (placement === "prefer-top") {
|
44 |
+
/// check if the toast has enough space to be placed above the anchor
|
45 |
+
const popoverHeight = popoverElement.getBoundingClientRect().height;
|
46 |
+
computedPlacement = anchorBbox.top > popoverHeight + HIT_ZONE_MARGIN ? "top" : "bottom";
|
47 |
+
} else if (placement === "prefer-bottom") {
|
48 |
+
/// check if the toast has enough space to be placed below the anchor
|
49 |
+
const popoverHeight = popoverElement.getBoundingClientRect().height;
|
50 |
+
computedPlacement =
|
51 |
+
anchorBbox.top + anchorBbox.height + popoverHeight + HIT_ZONE_MARGIN > pageHeight ? "top" : "bottom";
|
52 |
+
}
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
function updateAlignment(anchorBbox: DOMRect, pageWidth: number) {
|
57 |
+
if (alignment === "auto" && pageWidth > 0) {
|
58 |
+
const popoverWidth = popoverElement.getBoundingClientRect().width;
|
59 |
+
if (anchorBbox.left + popoverWidth > pageWidth - HIT_ZONE_MARGIN) {
|
60 |
+
computedAlignment = "end";
|
61 |
+
} else {
|
62 |
+
computedAlignment = "start";
|
63 |
+
}
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
async function updatePosition() {
|
68 |
+
if (anchorElement && !waitForContent) {
|
69 |
+
await tick();
|
70 |
+
|
71 |
+
const bbox = anchorElement.getBoundingClientRect();
|
72 |
+
updateAlignment(bbox, window.innerWidth);
|
73 |
+
updatePlacement(bbox, window.innerHeight);
|
74 |
+
|
75 |
+
left = bbox.left + window.scrollX;
|
76 |
+
top = bbox.top + window.scrollY;
|
77 |
+
width = bbox.width;
|
78 |
+
height = bbox.height;
|
79 |
+
|
80 |
+
/// shift the popover so the arrow is exaclty at the middle of the anchor
|
81 |
+
popoverShift = width / 2 - ARROW_SIZE / 2 - ARROW_PADDING;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
const debouncedShow = debounce(() => (isActive = true), 250);
|
86 |
+
|
87 |
+
function hide() {
|
88 |
+
if (!popoverElement?.matches(":hover")) {
|
89 |
+
isActive = false;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
const debouncedHide = debounce(hide, 250);
|
93 |
+
|
94 |
+
onMount(() => {
|
95 |
+
isTouchOnly = touchOnly && window.matchMedia("(any-hover: none)").matches;
|
96 |
+
|
97 |
+
if (!isTouchOnly) {
|
98 |
+
updatePosition();
|
99 |
+
if (anchorElement) {
|
100 |
+
anchorElement.addEventListener("mouseover", debouncedShow);
|
101 |
+
anchorElement.addEventListener("mouseleave", debouncedHide);
|
102 |
+
return () => {
|
103 |
+
anchorElement.removeEventListener("mouseover", debouncedShow);
|
104 |
+
anchorElement.removeEventListener("mouseleave", debouncedHide);
|
105 |
+
};
|
106 |
+
}
|
107 |
+
}
|
108 |
+
});
|
109 |
+
</script>
|
110 |
+
|
111 |
+
<svelte:window on:resize={() => dispatch("close")} on:scroll={() => dispatch("close")} />
|
112 |
+
|
113 |
+
<div class={isTouchOnly ? "hidden sm:contents" : "contents"} use:portalToBody>
|
114 |
+
<div
|
115 |
+
class="pointer-events-none absolute bg-transparent hidden"
|
116 |
+
class:hidden={!isActive}
|
117 |
+
style:top="{top}px"
|
118 |
+
style:left="{left}px"
|
119 |
+
style:width="{width}px"
|
120 |
+
style:height="{height}px"
|
121 |
+
>
|
122 |
+
<div
|
123 |
+
bind:this={popoverElement}
|
124 |
+
in:fade={{ duration: 100 }}
|
125 |
+
on:mouseleave={debouncedHide}
|
126 |
+
class="pointer-events-auto absolute z-10 transform
|
127 |
+
{computedPlacement === 'top' ? 'bottom-full -translate-y-3' : 'top-full translate-y-2.5'}
|
128 |
+
{computedAlignment === 'start' ? 'left-0' : computedAlignment === 'end' ? 'right-0' : 'left-1/2 -translate-x-1/2'}
|
129 |
+
{classNames}"
|
130 |
+
>
|
131 |
+
<div
|
132 |
+
class="absolute z-0 rotate-45 transform
|
133 |
+
{size === 'sm' ? 'h-2 w-2' : 'h-2.5 w-2.5 rounded-sm'}
|
134 |
+
{invertedColors ? 'bg-black dark:bg-gray-800' : 'border bg-white shadow dark:bg-gray-800'}
|
135 |
+
{computedPlacement === 'top' ? 'top-full -translate-y-1' : 'bottom-full translate-y-1'}
|
136 |
+
{computedAlignment === 'start' ? 'left-6' : computedAlignment === 'center' ? 'left-1/2' : 'right-6'}"
|
137 |
+
/>
|
138 |
+
<div
|
139 |
+
class="shadow-alternate-xl relative z-5 border font-normal leading-tight transition-opacity
|
140 |
+
{size === 'sm' ? 'rounded px-2 py-1.5' : 'rounded-xl p-4'}
|
141 |
+
{invertedColors ? 'border-black bg-black text-white dark:bg-gray-800' : 'bg-white text-black dark:bg-gray-925'}
|
142 |
+
"
|
143 |
+
>
|
144 |
+
<slot />
|
145 |
+
</div>
|
146 |
+
</div>
|
147 |
+
</div>
|
148 |
+
</div>
|
packages/widgets/src/lib/utils/ViewUtils.ts
CHANGED
@@ -156,6 +156,42 @@ export function onCmdEnter(node: HTMLElement, opts: { disabled: boolean }): Acti
|
|
156 |
};
|
157 |
}
|
158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
/**
|
160 |
* For Tailwind:
|
161 |
bg-blue-100 border-blue-100 dark:bg-blue-800 dark:border-blue-800
|
|
|
156 |
};
|
157 |
}
|
158 |
|
159 |
+
/**
|
160 |
+
* A debounce function that works in both browser and Nodejs.
|
161 |
+
*/
|
162 |
+
export function debounce<T extends unknown[]>(callback: (...rest: T) => unknown, limit: number): (...rest: T) => void {
|
163 |
+
let timer: ReturnType<typeof setTimeout>;
|
164 |
+
|
165 |
+
return function (...rest) {
|
166 |
+
clearTimeout(timer);
|
167 |
+
timer = setTimeout(() => {
|
168 |
+
callback(...rest);
|
169 |
+
}, limit);
|
170 |
+
};
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Teleports the children of a node to another node....
|
175 |
+
*/
|
176 |
+
export function portal(node: HTMLElement, targetNode: HTMLElement): { destroy: () => void } {
|
177 |
+
const portalChildren = [...node.children];
|
178 |
+
targetNode.append(...portalChildren);
|
179 |
+
return {
|
180 |
+
destroy() {
|
181 |
+
for (const portalChild of portalChildren) {
|
182 |
+
portalChild.remove();
|
183 |
+
}
|
184 |
+
},
|
185 |
+
};
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Teleports the children of a node under the body element
|
190 |
+
*/
|
191 |
+
export function portalToBody(node: HTMLElement): { destroy: () => void } {
|
192 |
+
return portal(node, document.body);
|
193 |
+
}
|
194 |
+
|
195 |
/**
|
196 |
* For Tailwind:
|
197 |
bg-blue-100 border-blue-100 dark:bg-blue-800 dark:border-blue-800
|
packages/widgets/src/routes/+page.svelte
CHANGED
@@ -6,6 +6,7 @@
|
|
6 |
import ModeSwitcher from "$lib/components/DemoThemeSwitcher/DemoThemeSwitcher.svelte";
|
7 |
import { onMount } from "svelte";
|
8 |
import { browser } from "$app/environment";
|
|
|
9 |
|
10 |
export let data;
|
11 |
let apiToken = data.session?.access_token || "";
|
@@ -627,6 +628,12 @@
|
|
627 |
<input class="form-input" type="text" bind:value={apiToken} placeholder="hf_..." on:change={storeHFToken} />
|
628 |
</label>
|
629 |
{/if}
|
|
|
|
|
|
|
|
|
|
|
|
|
630 |
|
631 |
<div>
|
632 |
<h1 class="mb-8 text-4xl font-semibold">Showcase of all types of inference widgets running</h1>
|
|
|
6 |
import ModeSwitcher from "$lib/components/DemoThemeSwitcher/DemoThemeSwitcher.svelte";
|
7 |
import { onMount } from "svelte";
|
8 |
import { browser } from "$app/environment";
|
9 |
+
import { isLoggedIn } from "$lib/components/InferenceWidget/stores.js";
|
10 |
|
11 |
export let data;
|
12 |
let apiToken = data.session?.access_token || "";
|
|
|
628 |
<input class="form-input" type="text" bind:value={apiToken} placeholder="hf_..." on:change={storeHFToken} />
|
629 |
</label>
|
630 |
{/if}
|
631 |
+
<label>
|
632 |
+
<div class="text-xl font-semibold">
|
633 |
+
isLoggedIn <span class="text-sm">(simulate isLoggedIn store by toggling)</span>
|
634 |
+
</div>
|
635 |
+
<input type="checkbox" bind:checked={$isLoggedIn} />
|
636 |
+
</label>
|
637 |
|
638 |
<div>
|
639 |
<h1 class="mb-8 text-4xl font-semibold">Showcase of all types of inference widgets running</h1>
|