From 54f7861b2e1d3c90cabc1c2aa9c3a507c6f31b4d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 16 Mar 2026 01:04:17 -0500 Subject: [PATCH] refac --- package-lock.json | 122 +++- package.json | 2 +- .../Message/UserStatusLinkPreview.svelte | 18 +- .../chat/Messages/Markdown/SourceToken.svelte | 3 +- .../chat/ModelSelector/Selector.svelte | 567 +++++++++--------- src/lib/components/common/Dropdown.svelte | 17 +- src/lib/components/common/Pagination.svelte | 54 +- 7 files changed, 442 insertions(+), 341 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a9867fc34..765db2286c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "@xyflow/svelte": "^0.1.19", "alpinejs": "^3.15.0", "async": "^3.2.5", - "bits-ui": "^0.21.15", + "bits-ui": "^2.0.0", "chart.js": "^4.5.0", "codemirror": "^6.0.1", "codemirror-lang-elixir": "^4.0.0", @@ -1770,10 +1770,11 @@ } }, "node_modules/@internationalized/date": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz", - "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.0.tgz", + "integrity": "sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/helpers": "^0.5.0" } @@ -2969,10 +2970,11 @@ "license": "MIT" }, "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", + "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -4982,37 +4984,27 @@ } }, "node_modules/bits-ui": { - "version": "0.21.15", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.15.tgz", - "integrity": "sha512-+m5WSpJnFdCcNdXSTIVC1WYBozipO03qRh03GFWgrdxoHiolCfwW71EYG4LPCWYPG6KcTZV0Cj6iHSiZ7cdKdg==", + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.16.3.tgz", + "integrity": "sha512-5hJ5dEhf5yPzkRFcxzgQHScGodeo0gK0MUUXrdLlRHWaBOBGZiacWLG96j/wwFatKwZvouw7q+sn14i0fx3RIg==", "license": "MIT", "dependencies": { - "@internationalized/date": "^3.5.1", - "@melt-ui/svelte": "0.76.2", - "nanoid": "^5.0.5" + "@floating-ui/core": "^1.7.1", + "@floating-ui/dom": "^1.7.1", + "esm-env": "^1.1.2", + "runed": "^0.35.1", + "svelte-toolbelt": "^0.10.6", + "tabbable": "^6.2.0" + }, + "engines": { + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/huntabyte" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.118" - } - }, - "node_modules/bits-ui/node_modules/@melt-ui/svelte": { - "version": "0.76.2", - "resolved": "https://registry.npmjs.org/@melt-ui/svelte/-/svelte-0.76.2.tgz", - "integrity": "sha512-7SbOa11tXUS95T3fReL+dwDs5FyJtCEqrqG3inRziDws346SYLsxOQ6HmX+4BkIsQh1R8U3XNa+EMmdMt38lMA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.3.1", - "@floating-ui/dom": "^1.4.5", - "@internationalized/date": "^3.5.0", - "dequal": "^2.0.3", - "focus-trap": "^7.5.2", - "nanoid": "^5.0.4" - }, - "peerDependencies": { - "svelte": ">=3 <5" + "@internationalized/date": "^3.8.1", + "svelte": "^5.33.0" } }, "node_modules/bl": { @@ -8611,6 +8603,12 @@ "node": ">=10" } }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -9662,6 +9660,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -11691,6 +11698,30 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/runed": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.35.1.tgz", + "integrity": "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==", + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "esm-env": "^1.0.0", + "lz-string": "^1.5.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.21.0", + "svelte": "^5.7.0" + }, + "peerDependenciesMeta": { + "@sveltejs/kit": { + "optional": true + } + } + }, "node_modules/rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", @@ -12600,6 +12631,15 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/stylis": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", @@ -12788,6 +12828,26 @@ "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1" } }, + "node_modules/svelte-toolbelt": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.10.6.tgz", + "integrity": "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==", + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.35.1", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.30.2" + } + }, "node_modules/svelte/node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", diff --git a/package.json b/package.json index 6f3cc7cf1c..9a11d852d0 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "@xyflow/svelte": "^0.1.19", "alpinejs": "^3.15.0", "async": "^3.2.5", - "bits-ui": "^0.21.15", + "bits-ui": "^2.0.0", "chart.js": "^4.5.0", "codemirror": "^6.0.1", "codemirror-lang-elixir": "^4.0.0", diff --git a/src/lib/components/channel/Messages/Message/UserStatusLinkPreview.svelte b/src/lib/components/channel/Messages/Message/UserStatusLinkPreview.svelte index 749ca8df10..0c8090f0d4 100644 --- a/src/lib/components/channel/Messages/Message/UserStatusLinkPreview.svelte +++ b/src/lib/components/channel/Messages/Message/UserStatusLinkPreview.svelte @@ -25,12 +25,14 @@ {#if user} - - - + + + + + {/if} diff --git a/src/lib/components/chat/Messages/Markdown/SourceToken.svelte b/src/lib/components/chat/Messages/Markdown/SourceToken.svelte index 557a0d38a2..7748e137ee 100644 --- a/src/lib/components/chat/Messages/Markdown/SourceToken.svelte +++ b/src/lib/components/chat/Messages/Markdown/SourceToken.svelte @@ -60,12 +60,12 @@ +
{#each token.citationIdentifiers ?? token.ids as identifier} @@ -77,6 +77,7 @@ {/each}
+
{/if} {:else} diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte index 7596c684a2..f918bcba4c 100644 --- a/src/lib/components/chat/ModelSelector/Selector.svelte +++ b/src/lib/components/chat/ModelSelector/Selector.svelte @@ -9,6 +9,7 @@ import Spinner from '$lib/components/common/Spinner.svelte'; import { flyAndScale } from '$lib/utils/transitions'; + import { createEventDispatcher, onMount, getContext, tick } from 'svelte'; import { goto } from '$app/navigation'; @@ -396,7 +397,12 @@ resetView(); }} - closeFocus={false} + onOpenChangeComplete={(open) => { + if (!open) { + // Replaces the old closeFocus={false} behavior - prevent focus jump back to trigger + document.getElementById(`model-selector-${id}-button`)?.blur(); + } + }} > - - {#if searchEnabled} -
- - - { - if (e.code === 'Enter' && filteredItems.length > 0) { - value = filteredItems[selectedModelIdx].value; - show = false; - return; // dont need to scroll on selection - } else if (e.code === 'ArrowDown') { - e.stopPropagation(); - selectedModelIdx = Math.min(selectedModelIdx + 1, filteredItems.length - 1); - } else if (e.code === 'ArrowUp') { - e.stopPropagation(); - selectedModelIdx = Math.max(selectedModelIdx - 1, 0); - } else { - // if the user types something, reset to the top selection. - selectedModelIdx = 0; - } - - const item = document.querySelector(`[data-arrow-selected="true"]`); - item?.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' }); - }} - /> -
- {/if} - -
- {#if tags && items.filter((item) => !(item.model?.info?.meta?.hidden ?? false)).length > 0} -
{ - if (e.deltaY !== 0) { - e.preventDefault(); - e.currentTarget.scrollLeft += e.deltaY; - } - }} - > + + + {#snippet child({ wrapperProps, props, open })} + {#if open} +
- {#if items.find((item) => item.model?.connection_type === 'local') || items.find((item) => item.model?.connection_type === 'external') || items.find((item) => item.model?.direct) || tags.length > 0} - - {/if} + + {#if searchEnabled} +
+ - {#if items.find((item) => item.model?.connection_type === 'local')} - - {/if} + { + if (e.code === 'Enter' && filteredItems.length > 0) { + value = filteredItems[selectedModelIdx].value; + show = false; + return; // dont need to scroll on selection + } else if (e.code === 'ArrowDown') { + e.stopPropagation(); + selectedModelIdx = Math.min(selectedModelIdx + 1, filteredItems.length - 1); + } else if (e.code === 'ArrowUp') { + e.stopPropagation(); + selectedModelIdx = Math.max(selectedModelIdx - 1, 0); + } else { + // if the user types something, reset to the top selection. + selectedModelIdx = 0; + } - {#if items.find((item) => item.model?.connection_type === 'external')} - - {/if} - - {#if items.find((item) => item.model?.direct)} - - {/if} - - {#each tags as tag} - - - - {/each} -
+ const item = document.querySelector(`[data-arrow-selected="true"]`); + item?.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' }); + }} + />
{/if} -
-
- {#if filteredItems.length === 0} - {#if items.length === 0 && $user?.role === 'admin'} -
-
- {$i18n.t('No models available')} -
-
- {$i18n.t('Connect to an AI provider to start chatting')} -
- { - show = false; - }} +
+ {#if tags && items.filter((item) => !(item.model?.info?.meta?.hidden ?? false)).length > 0} +
{ + if (e.deltaY !== 0) { + e.preventDefault(); + e.currentTarget.scrollLeft += e.deltaY; + } + }} + > + - {:else} -
-
- {$i18n.t('No results found')} + {#if items.find((item) => item.model?.connection_type === 'local') || items.find((item) => item.model?.connection_type === 'external') || items.find((item) => item.model?.direct) || tags.length > 0} + + {/if} + + {#if items.find((item) => item.model?.connection_type === 'local')} + + {/if} + + {#if items.find((item) => item.model?.connection_type === 'external')} + + {/if} + + {#if items.find((item) => item.model?.direct)} + + {/if} + + {#each tags as tag} + + + + {/each}
{/if} - {:else} - -
{ - listScrollTop = listContainer.scrollTop; - }} - > -
- {#each filteredItems.slice(visibleStart, visibleEnd) as item, i (item.value)} - {@const index = visibleStart + i} - { - value = item.value; - selectedModelIdx = index; +
- show = false; - }} - /> - {/each} -
-
- {/if} - - {#if !(searchValue.trim() in $MODEL_DOWNLOAD_POOL) && searchValue && ollamaVersion && $user?.role === 'admin'} - - - - {/if} - - {#each Object.keys($MODEL_DOWNLOAD_POOL) as model} -
-
-
- -
- -
-
-
- Downloading "{model}" -
- -
- {'pullProgress' in $MODEL_DOWNLOAD_POOL[model] - ? `(${$MODEL_DOWNLOAD_POOL[model].pullProgress}%)` - : ''} -
+
+ {#if filteredItems.length === 0} + {#if items.length === 0 && $user?.role === 'admin'} +
+
+ {$i18n.t('No models available')}
- - {#if 'digest' in $MODEL_DOWNLOAD_POOL[model] && $MODEL_DOWNLOAD_POOL[model].digest} -
- {$MODEL_DOWNLOAD_POOL[model].digest} -
- {/if} -
-
- -
- -
+ { - cancelModelPullHandler(model); + show = false; }} > -
+ {:else} +
+
+ {$i18n.t('No results found')} +
+
+ {/if} + {:else} + +
{ + listScrollTop = listContainer.scrollTop; + }} + > +
+ {#each filteredItems.slice(visibleStart, visibleEnd) as item, i (item.value)} + {@const index = visibleStart + i} + { + value = item.value; + selectedModelIdx = index; + + show = false; + }} + /> + {/each} +
+
+ {/if} + + {#if !(searchValue.trim() in $MODEL_DOWNLOAD_POOL) && searchValue && ollamaVersion && $user?.role === 'admin'} + + + + {/if} + + {#each Object.keys($MODEL_DOWNLOAD_POOL) as model} +
+
+
+ +
+ +
+
+
+ Downloading "{model}" +
+ +
+ {'pullProgress' in $MODEL_DOWNLOAD_POOL[model] + ? `(${$MODEL_DOWNLOAD_POOL[model].pullProgress}%)` + : ''} +
+
+ + {#if 'digest' in $MODEL_DOWNLOAD_POOL[model] && $MODEL_DOWNLOAD_POOL[model].digest} +
+ {$MODEL_DOWNLOAD_POOL[model].digest} +
+ {/if} +
+
+ +
+ + - + + + +
+
+ {/each} +
+ +
+ + - {/each} -
- -
- -