mirror of
https://github.com/open-webui/open-webui
synced 2025-02-16 10:12:37 +00:00
feat: chat controls ui
This commit is contained in:
parent
781ad70598
commit
b8d153ebb2
@ -60,14 +60,15 @@
|
||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||
import CallOverlay from './MessageInput/CallOverlay.svelte';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import ChatControls from './ChatControls.svelte';
|
||||
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
export let chatIdProp = '';
|
||||
let loaded = false;
|
||||
|
||||
const eventTarget = new EventTarget();
|
||||
|
||||
let showControls = false;
|
||||
let stopResponseFlag = false;
|
||||
let autoScroll = true;
|
||||
let processing = '';
|
||||
@ -1424,6 +1425,7 @@
|
||||
{title}
|
||||
bind:selectedModels
|
||||
bind:showModelSelector
|
||||
bind:showControls
|
||||
shareEnabled={messages.length > 0}
|
||||
{chat}
|
||||
{initNewChat}
|
||||
@ -1460,7 +1462,9 @@
|
||||
|
||||
<div class="flex flex-col flex-auto z-10">
|
||||
<div
|
||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10"
|
||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden {showControls
|
||||
? 'lg:pr-[28rem]'
|
||||
: ''} "
|
||||
id="messages-container"
|
||||
bind:this={messagesContainerElement}
|
||||
on:scroll={(e) => {
|
||||
@ -1485,26 +1489,31 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<MessageInput
|
||||
bind:files
|
||||
bind:prompt
|
||||
bind:autoScroll
|
||||
bind:selectedToolIds
|
||||
bind:webSearchEnabled
|
||||
bind:atSelectedModel
|
||||
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
|
||||
const model = $models.find((m) => m.id === e);
|
||||
if (model?.info?.meta?.toolIds ?? false) {
|
||||
return [...new Set([...a, ...model.info.meta.toolIds])];
|
||||
}
|
||||
return a;
|
||||
}, [])}
|
||||
transparentBackground={$settings?.backgroundImageUrl ?? false}
|
||||
{selectedModels}
|
||||
{messages}
|
||||
{submitPrompt}
|
||||
{stopResponse}
|
||||
/>
|
||||
|
||||
<div class={showControls ? 'lg:pr-[28rem]' : ''}>
|
||||
<MessageInput
|
||||
bind:files
|
||||
bind:prompt
|
||||
bind:autoScroll
|
||||
bind:selectedToolIds
|
||||
bind:webSearchEnabled
|
||||
bind:atSelectedModel
|
||||
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
|
||||
const model = $models.find((m) => m.id === e);
|
||||
if (model?.info?.meta?.toolIds ?? false) {
|
||||
return [...new Set([...a, ...model.info.meta.toolIds])];
|
||||
}
|
||||
return a;
|
||||
}, [])}
|
||||
transparentBackground={$settings?.backgroundImageUrl ?? false}
|
||||
{selectedModels}
|
||||
{messages}
|
||||
{submitPrompt}
|
||||
{stopResponse}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChatControls bind:show={showControls} />
|
||||
</div>
|
||||
{/if}
|
||||
|
61
src/lib/components/chat/ChatControls.svelte
Normal file
61
src/lib/components/chat/ChatControls.svelte
Normal file
@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import { slide } from 'svelte/transition';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import Controls from './Controls/Controls.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let show = false;
|
||||
|
||||
let largeScreen = false;
|
||||
|
||||
onMount(() => {
|
||||
// listen to resize 1024px
|
||||
const mediaQuery = window.matchMedia('(min-width: 1024px)');
|
||||
|
||||
const handleMediaQuery = (e) => {
|
||||
if (e.matches) {
|
||||
largeScreen = true;
|
||||
} else {
|
||||
largeScreen = false;
|
||||
}
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', handleMediaQuery);
|
||||
|
||||
handleMediaQuery(mediaQuery);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', handleMediaQuery);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if largeScreen}
|
||||
<div
|
||||
class="fixed h-screen max-h-[100dvh] min-h-screen z-50 top-0 right-0 {show
|
||||
? 'w-[28rem]'
|
||||
: 'w-0 translate-x-[28rem] '} transition"
|
||||
>
|
||||
<div class="px-6 pt-14 pb-8 h-full">
|
||||
<div
|
||||
class=" px-5 py-4 h-full dark:bg-gray-850 border border-gray-100 dark:border-gray-800 rounded-xl shadow-lg"
|
||||
>
|
||||
<Controls
|
||||
on:close={() => {
|
||||
show = false;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<Modal bind:show>
|
||||
<div class=" px-5 py-4 h-full">
|
||||
<Controls
|
||||
on:close={() => {
|
||||
show = false;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
{/if}
|
24
src/lib/components/chat/Controls/Controls.svelte
Normal file
24
src/lib/components/chat/Controls/Controls.svelte
Normal file
@ -0,0 +1,24 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
</script>
|
||||
|
||||
<div class=" dark:text-white">
|
||||
<div class="mb-2 flex justify-between items-center">
|
||||
<div class=" text-xl font-medium font-primary">Chat Controls</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
dispatch('close');
|
||||
}}
|
||||
>
|
||||
<XMark className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>coming soon</div>
|
||||
</div>
|
@ -34,7 +34,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full items-center md:items-start">
|
||||
<div class="flex flex-col w-full items-start">
|
||||
{#each selectedModels as selectedModel, selectedModelIdx}
|
||||
<div class="flex w-full max-w-fit">
|
||||
<div class="overflow-hidden w-full">
|
||||
|
17
src/lib/components/icons/AdjustmentsHorizontal.svelte
Normal file
17
src/lib/components/icons/AdjustmentsHorizontal.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
export let className = 'w-4 h-4';
|
||||
export let strokeWidth = '1.5';
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
class={className}
|
||||
stroke-width={strokeWidth}
|
||||
>
|
||||
<path
|
||||
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
|
||||
/>
|
||||
</svg>
|
@ -21,6 +21,7 @@
|
||||
import { page } from '$app/stores';
|
||||
import UserMenu from './Sidebar/UserMenu.svelte';
|
||||
import MenuLines from '../icons/MenuLines.svelte';
|
||||
import AdjustmentsHorizontal from '../icons/AdjustmentsHorizontal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@ -32,6 +33,7 @@
|
||||
export let selectedModels;
|
||||
|
||||
export let showModelSelector = true;
|
||||
export let showControls = false;
|
||||
|
||||
let showShareChatModal = false;
|
||||
let showDownloadChatModal = false;
|
||||
@ -58,6 +60,7 @@
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-hidden max-w-full">
|
||||
{#if showModelSelector}
|
||||
<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
|
||||
@ -101,12 +104,27 @@
|
||||
</button>
|
||||
</Menu>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Controls')}>
|
||||
<button
|
||||
id="new-chat-button"
|
||||
class=" flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
on:click={() => {
|
||||
showControls = !showControls;
|
||||
}}
|
||||
>
|
||||
<div class=" m-auto self-center">
|
||||
<AdjustmentsHorizontal className=" size-5" strokeWidth="0.5" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={$i18n.t('New Chat')}>
|
||||
<button
|
||||
id="new-chat-button"
|
||||
class=" flex {$showSidebar
|
||||
? 'md:hidden'
|
||||
: ''} cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
: ''} cursor-pointer px-2 py-2 rounded-xl text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
on:click={() => {
|
||||
initNewChat();
|
||||
}}
|
||||
|
@ -261,7 +261,7 @@
|
||||
alt="logo"
|
||||
/>
|
||||
</div>
|
||||
<div class=" self-center font-medium text-sm text-gray-850 dark:text-white">
|
||||
<div class=" self-center font-medium text-sm text-gray-850 dark:text-white font-primary">
|
||||
{$i18n.t('New Chat')}
|
||||
</div>
|
||||
<div class="self-center ml-auto">
|
||||
@ -339,7 +339,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex self-center">
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Workspace')}</div>
|
||||
<div class=" self-center font-medium text-sm font-primary">{$i18n.t('Workspace')}</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@ -533,7 +533,7 @@
|
||||
<div class="px-2.5">
|
||||
<!-- <hr class=" border-gray-900 mb-1 w-full" /> -->
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col font-primary">
|
||||
{#if $user !== undefined}
|
||||
<UserMenu
|
||||
role={$user.role}
|
||||
@ -556,50 +556,13 @@
|
||||
alt="User profile"
|
||||
/>
|
||||
</div>
|
||||
<div class=" self-center font-semibold">{$user.name}</div>
|
||||
<div class=" self-center font-medium">{$user.name}</div>
|
||||
</button>
|
||||
</UserMenu>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div
|
||||
id="sidebar-handle"
|
||||
class=" hidden md:fixed left-0 top-[50dvh] -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
|
||||
>
|
||||
<Tooltip
|
||||
placement="right"
|
||||
content={`${$showSidebar ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
|
||||
touch={false}
|
||||
>
|
||||
<button
|
||||
id="sidebar-toggle-button"
|
||||
class=" group"
|
||||
on:click={() => {
|
||||
showSidebar.set(!$showSidebar);
|
||||
}}
|
||||
><span class="" data-state="closed"
|
||||
><div
|
||||
class="flex h-[72px] w-8 items-center justify-center opacity-50 group-hover:opacity-100 transition"
|
||||
>
|
||||
<div class="flex h-6 w-6 flex-col items-center">
|
||||
<div
|
||||
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[0.15rem] {$showSidebar
|
||||
? 'group-hover:rotate-[15deg]'
|
||||
: 'group-hover:rotate-[-15deg]'}"
|
||||
/>
|
||||
<div
|
||||
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[-0.15rem] {$showSidebar
|
||||
? 'group-hover:rotate-[-15deg]'
|
||||
: 'group-hover:rotate-[15deg]'}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
<slot name="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full {className} text-sm rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
|
||||
class="w-full {className} text-sm rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow font-primary"
|
||||
sideOffset={8}
|
||||
side="bottom"
|
||||
align="start"
|
||||
|
Loading…
Reference in New Issue
Block a user