Thomas G. Lopes commited on
Commit
c3571d4
·
1 Parent(s): de05e6a

better loading logic

Browse files
package.json CHANGED
@@ -35,7 +35,7 @@
35
  "@samchon/openapi": "^4.7.1",
36
  "@sveltejs/adapter-auto": "^3.3.1",
37
  "@sveltejs/adapter-node": "^5.3.2",
38
- "@sveltejs/kit": "^2.41.0",
39
  "@sveltejs/vite-plugin-svelte": "^4.0.4",
40
  "@tailwindcss/container-queries": "^0.1.1",
41
  "@tailwindcss/postcss": "^4.0.9",
 
35
  "@samchon/openapi": "^4.7.1",
36
  "@sveltejs/adapter-auto": "^3.3.1",
37
  "@sveltejs/adapter-node": "^5.3.2",
38
+ "@sveltejs/kit": "^2.42.1",
39
  "@sveltejs/vite-plugin-svelte": "^4.0.4",
40
  "@tailwindcss/container-queries": "^0.1.1",
41
  "@tailwindcss/postcss": "^4.0.9",
pnpm-lock.yaml CHANGED
@@ -86,13 +86,13 @@ importers:
86
  version: 4.7.1
87
  '@sveltejs/adapter-auto':
88
  specifier: ^3.3.1
89
- version: 3.3.1(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))
90
  '@sveltejs/adapter-node':
91
  specifier: ^5.3.2
92
- version: 5.3.2(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))
93
  '@sveltejs/kit':
94
- specifier: ^2.41.0
95
- version: 2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
96
  '@sveltejs/vite-plugin-svelte':
97
  specifier: ^4.0.4
98
  version: 4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
@@ -942,8 +942,8 @@ packages:
942
  peerDependencies:
943
  '@sveltejs/kit': ^2.4.0
944
 
945
- '@sveltejs/kit@2.41.0':
946
- resolution: {integrity: sha512-dCLIRufAoc3bbKtHCSzDr3g41sPY0bNpxYbMd6XD2D7xSeVLgPvZ0qFtK8W2hhwr7oj8i/6NtlcB0EKFjpMMUg==}
947
  engines: {node: '>=18.13'}
948
  hasBin: true
949
  peerDependencies:
@@ -4076,20 +4076,20 @@ snapshots:
4076
  dependencies:
4077
  acorn: 8.15.0
4078
 
4079
- '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))':
4080
  dependencies:
4081
- '@sveltejs/kit': 2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
4082
  import-meta-resolve: 4.1.0
4083
 
4084
- '@sveltejs/adapter-node@5.3.2(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))':
4085
  dependencies:
4086
  '@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9)
4087
  '@rollup/plugin-json': 6.1.0(rollup@4.34.9)
4088
  '@rollup/plugin-node-resolve': 16.0.0(rollup@4.34.9)
4089
- '@sveltejs/kit': 2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
4090
  rollup: 4.34.9
4091
 
4092
- '@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))':
4093
  dependencies:
4094
  '@standard-schema/spec': 1.0.0
4095
  '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
 
86
  version: 4.7.1
87
  '@sveltejs/adapter-auto':
88
  specifier: ^3.3.1
89
+ version: 3.3.1(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))
90
  '@sveltejs/adapter-node':
91
  specifier: ^5.3.2
92
+ version: 5.3.2(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))
93
  '@sveltejs/kit':
94
+ specifier: ^2.42.1
95
+ version: 2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
96
  '@sveltejs/vite-plugin-svelte':
97
  specifier: ^4.0.4
98
  version: 4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
 
942
  peerDependencies:
943
  '@sveltejs/kit': ^2.4.0
944
 
945
+ '@sveltejs/kit@2.42.1':
946
+ resolution: {integrity: sha512-SoWdb/OxEKHMlXZ78eNZa4pR1YdOGnUfcFj5NylJv+ZaTrnHK5xapw+7RJP2MiS1D4T+sEH25/tmMtmEl8p6VA==}
947
  engines: {node: '>=18.13'}
948
  hasBin: true
949
  peerDependencies:
 
4076
  dependencies:
4077
  acorn: 8.15.0
4078
 
4079
+ '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))':
4080
  dependencies:
4081
+ '@sveltejs/kit': 2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
4082
  import-meta-resolve: 4.1.0
4083
 
4084
+ '@sveltejs/adapter-node@5.3.2(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))':
4085
  dependencies:
4086
  '@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9)
4087
  '@rollup/plugin-json': 6.1.0(rollup@4.34.9)
4088
  '@rollup/plugin-node-resolve': 16.0.0(rollup@4.34.9)
4089
+ '@sveltejs/kit': 2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))
4090
  rollup: 4.34.9
4091
 
4092
+ '@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.10)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))':
4093
  dependencies:
4094
  '@standard-schema/spec': 1.0.0
4095
  '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
src/lib/components/inference-playground/hf-token-modal.svelte CHANGED
@@ -1,121 +1,124 @@
1
  <script lang="ts">
2
- import { createBubbler, preventDefault } from "svelte/legacy";
3
-
4
- const bubble = createBubbler();
5
  import { clickOutside } from "$lib/attachments/click-outside.js";
6
- import { createEventDispatcher, onDestroy, onMount } from "svelte";
7
 
8
- import IconCross from "~icons/carbon/close";
9
  import { autofocus } from "$lib/attachments/autofocus.js";
10
-
11
- interface Props {
12
- storeLocallyHfToken?: boolean;
13
- }
14
-
15
- let { storeLocallyHfToken = $bindable(false) }: Props = $props();
16
 
17
  let backdropEl = $state<HTMLDivElement>();
18
  let modalEl = $state<HTMLDivElement>();
19
 
20
- const dispatch = createEventDispatcher<{ close: void }>();
21
-
22
  function handleKeydown(event: KeyboardEvent) {
23
  const { key } = event;
24
  if (key === "Escape") {
25
  event.preventDefault();
26
- dispatch("close");
27
  }
28
  }
29
 
30
- onMount(() => {
31
- document.getElementById("app")?.setAttribute("inert", "true");
32
- });
33
-
34
- onDestroy(() => {
35
- // remove inert attribute if this is the last modal
36
- if (document.querySelectorAll('[role="dialog"]:not(#app *)').length === 1) {
37
  document.getElementById("app")?.removeAttribute("inert");
38
  }
39
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </script>
41
 
42
- <div
43
- id="default-modal"
44
- aria-hidden="true"
45
- class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/85"
46
- bind:this={backdropEl}
47
- >
48
  <div
49
- role="dialog"
50
- tabindex="-1"
51
- class="relative max-h-full w-full max-w-xl p-4 outline-hidden"
52
- bind:this={modalEl}
53
- onkeydown={handleKeydown}
54
- {@attach clickOutside(() => dispatch("close"))}
55
  >
56
- <form onsubmit={preventDefault(bubble("submit"))} class="relative rounded-lg bg-white shadow-sm dark:bg-gray-900">
57
- <div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-800">
58
- <h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white">
59
- <img
60
- alt="Hugging Face's logo"
61
- class="w-7"
62
- src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
63
- /> Add a Hugging Face Token
64
- </h3>
65
- <button
66
- type="button"
67
- onclick={() => dispatch("close")}
68
- class="ms-auto 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"
69
- >
70
- <div class="text-xl">
71
- <IconCross />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  </div>
73
- <span class="sr-only">Close modal</span>
74
- </button>
75
- </div>
76
- <!-- Modal body -->
77
- <div class="p-4 md:p-5">
78
- <p class="mb-5 text-base leading-relaxed text-gray-800 2xl:text-balance dark:text-gray-300">
79
- You need a free Hugging Face token to use this application. <strong class="font-semibold"
80
- >Make sure you create a token with Inference API permission.</strong
81
- ><br /> Your token is kept safe by only being used from your browser.
82
- </p>
83
- <div>
84
- <label for="hf-token" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">
85
- Hugging Face Token
86
- </label>
87
- <input
88
- required
89
- placeholder="Enter HF Token"
90
- type="text"
91
- id="hf-token"
92
- name="hf-token"
93
- class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
94
- {@attach autofocus()}
95
- />
96
  </div>
97
- <label class="mt-4 flex items-center gap-x-1.5 text-gray-900 dark:text-gray-200">
98
- <input type="checkbox" bind:checked={storeLocallyHfToken} />
99
- <p class="text-sm leading-none">Save to local storage for future use</p></label
100
- >
101
- </div>
102
 
103
- <!-- Modal footer -->
104
- <div class="flex items-center justify-between rounded-b border-t border-gray-200 p-4 md:p-5 dark:border-gray-800">
105
- <a
106
- href="https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained"
107
- tabindex="-1"
108
- target="_blank"
109
- class="rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
110
- >Create new token</a
111
  >
 
 
 
 
 
 
 
112
 
113
- <button
114
- type="submit"
115
- class="rounded-lg bg-black px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-900 focus:ring-4 focus:ring-gray-300 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700"
116
- >Submit</button
117
- >
118
- </div>
119
- </form>
 
120
  </div>
121
- </div>
 
1
  <script lang="ts">
 
 
 
2
  import { clickOutside } from "$lib/attachments/click-outside.js";
 
3
 
 
4
  import { autofocus } from "$lib/attachments/autofocus.js";
5
+ import { token } from "$lib/state/token.svelte";
6
+ import IconCross from "~icons/carbon/close";
 
 
 
 
7
 
8
  let backdropEl = $state<HTMLDivElement>();
9
  let modalEl = $state<HTMLDivElement>();
10
 
 
 
11
  function handleKeydown(event: KeyboardEvent) {
12
  const { key } = event;
13
  if (key === "Escape") {
14
  event.preventDefault();
15
+ token.showModal = false;
16
  }
17
  }
18
 
19
+ $effect(() => {
20
+ if (token.showModal) {
21
+ document.getElementById("app")?.setAttribute("inert", "true");
22
+ } else {
 
 
 
23
  document.getElementById("app")?.removeAttribute("inert");
24
  }
25
  });
26
+
27
+ function handleTokenSubmit(e: Event) {
28
+ e.preventDefault();
29
+ const form = e.target as HTMLFormElement;
30
+ const formData = new FormData(form);
31
+ const submittedHfToken = (formData.get("hf-token") as string).trim() ?? "";
32
+ const RE_HF_TOKEN = /\bhf_[a-zA-Z0-9]{34}\b/;
33
+ if (RE_HF_TOKEN.test(submittedHfToken)) {
34
+ token.value = submittedHfToken;
35
+ } else {
36
+ alert("Please provide a valid HF token.");
37
+ }
38
+ }
39
  </script>
40
 
41
+ {#if token.showModal}
 
 
 
 
 
42
  <div
43
+ id="default-modal"
44
+ aria-hidden="true"
45
+ class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/85"
46
+ bind:this={backdropEl}
 
 
47
  >
48
+ <div
49
+ role="dialog"
50
+ tabindex="-1"
51
+ class="relative max-h-full w-full max-w-xl p-4 outline-hidden"
52
+ bind:this={modalEl}
53
+ onkeydown={handleKeydown}
54
+ {@attach clickOutside(() => (token.showModal = false))}
55
+ >
56
+ <form onsubmit={handleTokenSubmit} class="relative rounded-lg bg-white shadow-sm dark:bg-gray-900">
57
+ <div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-800">
58
+ <h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white">
59
+ <img
60
+ alt="Hugging Face's logo"
61
+ class="w-7"
62
+ src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
63
+ /> Add a Hugging Face Token
64
+ </h3>
65
+ <button
66
+ type="button"
67
+ onclick={() => (token.showModal = false)}
68
+ class="ms-auto 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"
69
+ >
70
+ <div class="text-xl">
71
+ <IconCross />
72
+ </div>
73
+ <span class="sr-only">Close modal</span>
74
+ </button>
75
+ </div>
76
+ <!-- Modal body -->
77
+ <div class="p-4 md:p-5">
78
+ <p class="mb-5 text-base leading-relaxed text-gray-800 2xl:text-balance dark:text-gray-300">
79
+ You need a free Hugging Face token to use this application. <strong class="font-semibold"
80
+ >Make sure you create a token with Inference API permission.</strong
81
+ ><br /> Your token is kept safe by only being used from your browser.
82
+ </p>
83
+ <div>
84
+ <label for="hf-token" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">
85
+ Hugging Face Token
86
+ </label>
87
+ <input
88
+ required
89
+ placeholder="Enter HF Token"
90
+ type="text"
91
+ id="hf-token"
92
+ name="hf-token"
93
+ class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
94
+ {@attach autofocus()}
95
+ />
96
  </div>
97
+ <label class="mt-4 flex items-center gap-x-1.5 text-gray-900 dark:text-gray-200">
98
+ <input type="checkbox" bind:checked={token.writeToLocalStorage} />
99
+ <p class="text-sm leading-none">Save to local storage for future use</p></label
100
+ >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  </div>
 
 
 
 
 
102
 
103
+ <!-- Modal footer -->
104
+ <div
105
+ class="flex items-center justify-between rounded-b border-t border-gray-200 p-4 md:p-5 dark:border-gray-800"
 
 
 
 
 
106
  >
107
+ <a
108
+ href="https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained"
109
+ tabindex="-1"
110
+ target="_blank"
111
+ class="rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
112
+ >Create new token</a
113
+ >
114
 
115
+ <button
116
+ type="submit"
117
+ class="rounded-lg bg-black px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-900 focus:ring-4 focus:ring-gray-300 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700"
118
+ >Submit</button
119
+ >
120
+ </div>
121
+ </form>
122
+ </div>
123
  </div>
124
+ {/if}
src/lib/components/inference-playground/playground.svelte CHANGED
@@ -1,35 +1,34 @@
1
  <script lang="ts">
2
  import { observe, observed, ObservedElements } from "$lib/attachments/observe.svelte.js";
 
3
  import { conversations } from "$lib/state/conversations.svelte";
4
  import { projects } from "$lib/state/projects.svelte";
5
  import { token } from "$lib/state/token.svelte.js";
6
  import { isHFModel } from "$lib/types.js";
7
  import { iterate } from "$lib/utils/array.js";
8
  import { isSystemPromptSupported } from "$lib/utils/business.svelte.js";
 
9
  import IconExternal from "~icons/carbon/arrow-up-right";
10
  import IconWaterfall from "~icons/carbon/chart-waterfall";
 
11
  import IconCode from "~icons/carbon/code";
12
  import IconCompare from "~icons/carbon/compare";
13
  import IconInfo from "~icons/carbon/information";
14
  import IconSettings from "~icons/carbon/settings";
15
  import IconShare from "~icons/carbon/share";
16
  import { default as IconDelete } from "~icons/carbon/trash-can";
 
17
  import { showShareModal } from "../share-modal.svelte";
18
  import Toaster from "../toaster.svelte";
19
  import Tooltip from "../tooltip.svelte";
 
20
  import PlaygroundConversationHeader from "./conversation-header.svelte";
21
  import PlaygroundConversation from "./conversation.svelte";
22
  import GenerationConfig from "./generation-config.svelte";
23
- import HFTokenModal from "./hf-token-modal.svelte";
24
  import ModelSelectorModal from "./model-selector-modal.svelte";
25
  import ModelSelector from "./model-selector.svelte";
26
  import ProjectSelect from "./project-select.svelte";
27
- import BillingModal from "./billing-modal.svelte";
28
- import BillingIndicator from "../billing-indicator.svelte";
29
- import { TEST_IDS } from "$lib/constants.js";
30
- import MessageTextarea from "./message-textarea.svelte";
31
- import { atLeastNDecimals } from "$lib/utils/number.js";
32
- import IconClose from "~icons/carbon/close";
33
 
34
  let viewCode = $state(false);
35
  let viewSettings = $state(false);
@@ -39,30 +38,8 @@
39
 
40
  const systemPromptSupported = $derived(conversations.active.some(c => isSystemPromptSupported(c.model)));
41
  const compareActive = $derived(conversations.active.length === 2);
42
-
43
- function handleTokenSubmit(e: Event) {
44
- const form = e.target as HTMLFormElement;
45
- const formData = new FormData(form);
46
- const submittedHfToken = (formData.get("hf-token") as string).trim() ?? "";
47
- const RE_HF_TOKEN = /\bhf_[a-zA-Z0-9]{34}\b/;
48
- if (RE_HF_TOKEN.test(submittedHfToken)) {
49
- token.value = submittedHfToken;
50
- // TODO: Only submit when previous action was trying to submit
51
- // submit();
52
- } else {
53
- alert("Please provide a valid HF token.");
54
- }
55
- }
56
  </script>
57
 
58
- {#if token.showModal}
59
- <HFTokenModal
60
- bind:storeLocallyHfToken={token.writeToLocalStorage}
61
- on:close={() => (token.showModal = false)}
62
- on:submit={handleTokenSubmit}
63
- />
64
- {/if}
65
-
66
  <div
67
  class={[
68
  "motion-safe:animate-fade-in grid h-dvh divide-gray-200 overflow-hidden bg-gray-100/50",
 
1
  <script lang="ts">
2
  import { observe, observed, ObservedElements } from "$lib/attachments/observe.svelte.js";
3
+ import { TEST_IDS } from "$lib/constants.js";
4
  import { conversations } from "$lib/state/conversations.svelte";
5
  import { projects } from "$lib/state/projects.svelte";
6
  import { token } from "$lib/state/token.svelte.js";
7
  import { isHFModel } from "$lib/types.js";
8
  import { iterate } from "$lib/utils/array.js";
9
  import { isSystemPromptSupported } from "$lib/utils/business.svelte.js";
10
+ import { atLeastNDecimals } from "$lib/utils/number.js";
11
  import IconExternal from "~icons/carbon/arrow-up-right";
12
  import IconWaterfall from "~icons/carbon/chart-waterfall";
13
+ import IconClose from "~icons/carbon/close";
14
  import IconCode from "~icons/carbon/code";
15
  import IconCompare from "~icons/carbon/compare";
16
  import IconInfo from "~icons/carbon/information";
17
  import IconSettings from "~icons/carbon/settings";
18
  import IconShare from "~icons/carbon/share";
19
  import { default as IconDelete } from "~icons/carbon/trash-can";
20
+ import BillingIndicator from "../billing-indicator.svelte";
21
  import { showShareModal } from "../share-modal.svelte";
22
  import Toaster from "../toaster.svelte";
23
  import Tooltip from "../tooltip.svelte";
24
+ import BillingModal from "./billing-modal.svelte";
25
  import PlaygroundConversationHeader from "./conversation-header.svelte";
26
  import PlaygroundConversation from "./conversation.svelte";
27
  import GenerationConfig from "./generation-config.svelte";
28
+ import MessageTextarea from "./message-textarea.svelte";
29
  import ModelSelectorModal from "./model-selector-modal.svelte";
30
  import ModelSelector from "./model-selector.svelte";
31
  import ProjectSelect from "./project-select.svelte";
 
 
 
 
 
 
32
 
33
  let viewCode = $state(false);
34
  let viewSettings = $state(false);
 
38
 
39
  const systemPromptSupported = $derived(conversations.active.some(c => isSystemPromptSupported(c.model)));
40
  const compareActive = $derived(conversations.active.length === 2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  </script>
42
 
 
 
 
 
 
 
 
 
43
  <div
44
  class={[
45
  "motion-safe:animate-fade-in grid h-dvh divide-gray-200 overflow-hidden bg-gray-100/50",
src/lib/state/models.svelte.ts CHANGED
@@ -16,6 +16,8 @@ class Models {
16
  nonTrending = $derived(this.remote.filter(m => !this.trending.includes(m)).toSorted(trendingSort));
17
  all = $derived([...this.remote, ...this.custom]);
18
 
 
 
19
  constructor() {
20
  const savedData = localStorage.getItem(LOCAL_STORAGE_KEY);
21
  if (!savedData) return;
@@ -30,10 +32,16 @@ class Models {
30
  }
31
 
32
  async load() {
33
- await Promise.all([getModels(), getRouterData()]).then(([models, data]) => {
 
 
 
 
34
  this.remote = models;
35
  this.routerData = data;
36
  });
 
 
37
  }
38
 
39
  #custom = $state.raw<CustomModel[]>([]);
 
16
  nonTrending = $derived(this.remote.filter(m => !this.trending.includes(m)).toSorted(trendingSort));
17
  all = $derived([...this.remote, ...this.custom]);
18
 
19
+ loading: Promise<void> | null = null;
20
+
21
  constructor() {
22
  const savedData = localStorage.getItem(LOCAL_STORAGE_KEY);
23
  if (!savedData) return;
 
32
  }
33
 
34
  async load() {
35
+ if (this.loading || this.remote.length > 0) {
36
+ return await this.loading;
37
+ }
38
+
39
+ this.loading = Promise.all([getModels(), getRouterData()]).then(([models, data]) => {
40
  this.remote = models;
41
  this.routerData = data;
42
  });
43
+ await this.loading;
44
+ this.loading = null;
45
  }
46
 
47
  #custom = $state.raw<CustomModel[]>([]);
src/routes/+layout.svelte CHANGED
@@ -1,14 +1,13 @@
1
  <script lang="ts">
2
  import IconLoadingLoop from "~icons/line-md/loading-loop";
3
  import "../app.css";
4
- import { models } from "$lib/state/models.svelte";
5
 
6
  interface Props {
7
  children?: import("svelte").Snippet;
8
  }
9
 
10
  let { children }: Props = $props();
11
- models.load();
12
  </script>
13
 
14
  <svelte:boundary>
@@ -18,3 +17,5 @@
18
  <IconLoadingLoop class="abs-center absolute text-6xl dark:text-blue-300" />
19
  {/snippet}
20
  </svelte:boundary>
 
 
 
1
  <script lang="ts">
2
  import IconLoadingLoop from "~icons/line-md/loading-loop";
3
  import "../app.css";
4
+ import HfTokenModal from "$lib/components/inference-playground/hf-token-modal.svelte";
5
 
6
  interface Props {
7
  children?: import("svelte").Snippet;
8
  }
9
 
10
  let { children }: Props = $props();
 
11
  </script>
12
 
13
  <svelte:boundary>
 
17
  <IconLoadingLoop class="abs-center absolute text-6xl dark:text-blue-300" />
18
  {/snippet}
19
  </svelte:boundary>
20
+
21
+ <HfTokenModal />
src/routes/canvas/+page.svelte CHANGED
@@ -1,4 +1,5 @@
1
  <script lang="ts">
 
2
  import type { Node } from "@xyflow/svelte";
3
  import { Background, Controls, MiniMap, SvelteFlow } from "@xyflow/svelte";
4
  import "@xyflow/svelte/dist/style.css";
 
1
  <script lang="ts">
2
+ import "@xyflow/svelte/dist/style.css";
3
  import type { Node } from "@xyflow/svelte";
4
  import { Background, Controls, MiniMap, SvelteFlow } from "@xyflow/svelte";
5
  import "@xyflow/svelte/dist/style.css";
src/routes/canvas/chat-node.svelte CHANGED
@@ -20,6 +20,8 @@
20
  };
21
  let { id, data }: Props = $props();
22
 
 
 
23
  let { updateNodeData, updateNode, getNode } = useSvelteFlow();
24
  onMount(() => {
25
  if (!data.modelId) data.modelId = models.trending[0]?.id;
 
20
  };
21
  let { id, data }: Props = $props();
22
 
23
+ await models.load();
24
+
25
  let { updateNodeData, updateNode, getNode } = useSvelteFlow();
26
  onMount(() => {
27
  if (!data.modelId) data.modelId = models.trending[0]?.id;