refac
This commit is contained in:
@@ -97,11 +97,13 @@
|
|||||||
<div
|
<div
|
||||||
class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400 line-clamp-3 markdown"
|
class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400 line-clamp-3 markdown"
|
||||||
>
|
>
|
||||||
{@html DOMPurify.sanitize(marked.parse(
|
{@html DOMPurify.sanitize(
|
||||||
|
marked.parse(
|
||||||
sanitizeResponseContent(
|
sanitizeResponseContent(
|
||||||
models[selectedModelIdx]?.info?.meta?.description
|
models[selectedModelIdx]?.info?.meta?.description
|
||||||
).replaceAll('\n', '<br>')
|
).replaceAll('\n', '<br>')
|
||||||
))}
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{#if models[selectedModelIdx]?.info?.meta?.user}
|
{#if models[selectedModelIdx]?.info?.meta?.user}
|
||||||
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
|
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { DropdownMenu } from 'bits-ui';
|
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
|
|
||||||
@@ -63,6 +62,64 @@
|
|||||||
let tagsContainerElement;
|
let tagsContainerElement;
|
||||||
|
|
||||||
let show = false;
|
let show = false;
|
||||||
|
let triggerElement: HTMLElement | null = null;
|
||||||
|
let contentElement: HTMLElement | null = null;
|
||||||
|
let dropdownPosition = { top: 0, left: 0, width: 0 };
|
||||||
|
|
||||||
|
const portal = (node: HTMLElement) => {
|
||||||
|
document.body.appendChild(node);
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatePosition = () => {
|
||||||
|
if (!show || !triggerElement) return;
|
||||||
|
const rect = triggerElement.getBoundingClientRect();
|
||||||
|
dropdownPosition = {
|
||||||
|
top: rect.bottom + 2,
|
||||||
|
left: $mobile ? 8 : rect.left,
|
||||||
|
width: $mobile ? window.innerWidth - 16 : 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleOpen = () => {
|
||||||
|
show = !show;
|
||||||
|
if (show) {
|
||||||
|
searchValue = '';
|
||||||
|
listScrollTop = 0;
|
||||||
|
resetView();
|
||||||
|
updatePosition();
|
||||||
|
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
|
||||||
|
} else {
|
||||||
|
document.getElementById(`model-selector-${id}-button`)?.blur();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerDown = (e: PointerEvent) => {
|
||||||
|
if (!show) return;
|
||||||
|
const target = e.target as Node;
|
||||||
|
if (
|
||||||
|
(triggerElement && triggerElement.contains(target)) ||
|
||||||
|
(contentElement && contentElement.contains(target))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
show = false;
|
||||||
|
document.getElementById(`model-selector-${id}-button`)?.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeydown = (e: KeyboardEvent) => {
|
||||||
|
if (show && e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
show = false;
|
||||||
|
document.getElementById(`model-selector-${id}-button`)?.blur();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let tags = [];
|
let tags = [];
|
||||||
|
|
||||||
let selectedModel = '';
|
let selectedModel = '';
|
||||||
@@ -437,30 +494,26 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DropdownMenu.Root
|
<svelte:window
|
||||||
bind:open={show}
|
on:pointerdown={handlePointerDown}
|
||||||
onOpenChange={(open) => {
|
on:keydown={handleKeydown}
|
||||||
if (open) {
|
on:resize={updatePosition}
|
||||||
searchValue = '';
|
/>
|
||||||
listScrollTop = 0;
|
|
||||||
resetView();
|
<div class="relative w-full">
|
||||||
}
|
<button
|
||||||
}}
|
bind:this={triggerElement}
|
||||||
onOpenChangeComplete={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
// Replaces the old closeFocus={false} behavior - prevent focus jump back to trigger
|
|
||||||
document.getElementById(`model-selector-${id}-button`)?.blur();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DropdownMenu.Trigger
|
|
||||||
class="relative w-full {($settings?.highContrastMode ?? false)
|
class="relative w-full {($settings?.highContrastMode ?? false)
|
||||||
? ''
|
? ''
|
||||||
: 'outline-hidden focus:outline-hidden'}"
|
: 'outline-hidden focus:outline-hidden'}"
|
||||||
aria-label={selectedModel
|
aria-label={selectedModel
|
||||||
? $i18n.t('Selected model: {{modelName}}', { modelName: selectedModel.label })
|
? $i18n.t('Selected model: {{modelName}}', { modelName: selectedModel.label })
|
||||||
: placeholder}
|
: placeholder}
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
aria-expanded={show}
|
||||||
id="model-selector-{id}-button"
|
id="model-selector-{id}-button"
|
||||||
|
type="button"
|
||||||
|
on:click={toggleOpen}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex w-full text-left px-0.5 bg-transparent truncate {triggerClassName} justify-between {($settings?.highContrastMode ??
|
class="flex w-full text-left px-0.5 bg-transparent truncate {triggerClassName} justify-between {($settings?.highContrastMode ??
|
||||||
@@ -483,33 +536,18 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<ChevronDown className=" self-center ml-2 size-3" strokeWidth="2.5" />
|
<ChevronDown className=" self-center ml-2 size-3" strokeWidth="2.5" />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Trigger>
|
</button>
|
||||||
|
|
||||||
<DropdownMenu.Portal>
|
{#if show}
|
||||||
<DropdownMenu.Content
|
|
||||||
forceMount
|
|
||||||
trapFocus={false}
|
|
||||||
preventScroll={false}
|
|
||||||
side="bottom"
|
|
||||||
align={$mobile ? 'center' : 'start'}
|
|
||||||
sideOffset={2}
|
|
||||||
alignOffset={-1}
|
|
||||||
onOpenAutoFocus={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{#snippet child({ wrapperProps, props, open })}
|
|
||||||
{#if open}
|
|
||||||
<div
|
<div
|
||||||
{...wrapperProps}
|
use:portal
|
||||||
style="{wrapperProps.style ?? ''}{$mobile
|
bind:this={contentElement}
|
||||||
? '; left: 0.5rem !important; width: calc(100vw - 1rem) !important;'
|
style="position: fixed; z-index: 9999; top: {dropdownPosition.top}px; left: {dropdownPosition.left}px;{$mobile
|
||||||
|
? ` width: ${dropdownPosition.width}px;`
|
||||||
: ''}"
|
: ''}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
{...props}
|
class="z-40 {$mobile
|
||||||
class="{props.class} z-40 {$mobile
|
|
||||||
? `w-full`
|
? `w-full`
|
||||||
: `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-2xl bg-white dark:bg-gray-850 dark:text-white shadow-lg outline-hidden"
|
: `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-2xl bg-white dark:bg-gray-850 dark:text-white shadow-lg outline-hidden"
|
||||||
transition:flyAndScale
|
transition:flyAndScale
|
||||||
@@ -533,10 +571,7 @@
|
|||||||
return; // dont need to scroll on selection
|
return; // dont need to scroll on selection
|
||||||
} else if (e.code === 'ArrowDown') {
|
} else if (e.code === 'ArrowDown') {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
selectedModelIdx = Math.min(
|
selectedModelIdx = Math.min(selectedModelIdx + 1, filteredItems.length - 1);
|
||||||
selectedModelIdx + 1,
|
|
||||||
filteredItems.length - 1
|
|
||||||
);
|
|
||||||
} else if (e.code === 'ArrowUp') {
|
} else if (e.code === 'ArrowUp') {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
selectedModelIdx = Math.max(selectedModelIdx - 1, 0);
|
selectedModelIdx = Math.max(selectedModelIdx - 1, 0);
|
||||||
@@ -810,7 +845,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/snippet}
|
</div>
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Portal>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
|
|||||||
@@ -175,11 +175,13 @@
|
|||||||
<div
|
<div
|
||||||
class="mt-0.5 px-2 text-sm font-normal text-gray-500 dark:text-gray-400 line-clamp-2 max-w-xl markdown"
|
class="mt-0.5 px-2 text-sm font-normal text-gray-500 dark:text-gray-400 line-clamp-2 max-w-xl markdown"
|
||||||
>
|
>
|
||||||
{@html DOMPurify.sanitize(marked.parse(
|
{@html DOMPurify.sanitize(
|
||||||
|
marked.parse(
|
||||||
sanitizeResponseContent(
|
sanitizeResponseContent(
|
||||||
models[selectedModelIdx]?.info?.meta?.description ?? ''
|
models[selectedModelIdx]?.info?.meta?.description ?? ''
|
||||||
).replaceAll('\n', '<br>')
|
).replaceAll('\n', '<br>')
|
||||||
))}
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
|||||||
@@ -874,7 +874,9 @@
|
|||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 px-3.5 md:pl-0 md:pr-4.5 md:min-h-[min(42rem,calc(100dvh-10rem))] max-h-[min(42rem,calc(100dvh-10rem))] overflow-y-auto">
|
<div
|
||||||
|
class="flex-1 px-3.5 md:pl-0 md:pr-4.5 md:min-h-[min(42rem,calc(100dvh-10rem))] max-h-[min(42rem,calc(100dvh-10rem))] overflow-y-auto"
|
||||||
|
>
|
||||||
{#if selectedTab === 'general'}
|
{#if selectedTab === 'general'}
|
||||||
<General
|
<General
|
||||||
{getModels}
|
{getModels}
|
||||||
|
|||||||
@@ -41,9 +41,9 @@
|
|||||||
style="white-space: pre-wrap;"
|
style="white-space: pre-wrap;"
|
||||||
>
|
>
|
||||||
{#if ($config?.ui?.pending_user_overlay_content ?? '').trim() !== ''}
|
{#if ($config?.ui?.pending_user_overlay_content ?? '').trim() !== ''}
|
||||||
{@html DOMPurify.sanitize(marked.parse(
|
{@html DOMPurify.sanitize(
|
||||||
($config?.ui?.pending_user_overlay_content ?? '').replace(/\n/g, '<br>')
|
marked.parse(($config?.ui?.pending_user_overlay_content ?? '').replace(/\n/g, '<br>'))
|
||||||
))}
|
)}
|
||||||
{:else}
|
{:else}
|
||||||
{$i18n.t('Your account status is currently pending activation.')}{'\n'}{$i18n.t(
|
{$i18n.t('Your account status is currently pending activation.')}{'\n'}{$i18n.t(
|
||||||
'To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.'
|
'To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.'
|
||||||
|
|||||||
Reference in New Issue
Block a user