Browse Source

feat: chat controls ui

Timothy J. Baek 9 months ago
parent
commit
b8d153ebb2

+ 31 - 22
src/lib/components/chat/Chat.svelte

@@ -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 - 0
src/lib/components/chat/ChatControls.svelte

@@ -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 - 0
src/lib/components/chat/Controls/Controls.svelte

@@ -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>

+ 1 - 1
src/lib/components/chat/ModelSelector.svelte

@@ -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 - 0
src/lib/components/icons/AdjustmentsHorizontal.svelte

@@ -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>

+ 19 - 1
src/lib/components/layout/Navbar.svelte

@@ -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();
 						}}

+ 4 - 41
src/lib/components/layout/Sidebar.svelte

@@ -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>

+ 1 - 1
src/lib/components/layout/Sidebar/UserMenu.svelte

@@ -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"