Unverified Commit 38ff3209 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #1881 from open-webui/dev

0.1.123
parents c9589e21 2789102d
......@@ -137,20 +137,22 @@
.getElementById(`message-${message.id}`)
?.getElementsByClassName('chat-assistant');
for (const element of chatMessageElements) {
auto_render(element, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{ left: '$$', right: '$$', display: false },
{ left: '$ ', right: ' $', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: false },
{ left: '[ ', right: ' ]', display: false }
],
// • rendering keys, e.g.:
throwOnError: false
});
if (chatMessageElements) {
for (const element of chatMessageElements) {
auto_render(element, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{ left: '$$', right: '$$', display: false },
{ left: '$ ', right: ' $', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: false },
{ left: '[ ', right: ' ]', display: false }
],
// • rendering keys, e.g.:
throwOnError: false
});
}
}
};
......@@ -325,7 +327,8 @@
{#key message.id}
<div class=" flex w-full message-{message.id}" id="message-{message.id}">
<ProfileImage
src={modelfiles[message.model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`}
src={modelfiles[message.model]?.imageUrl ??
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
/>
<div class="w-full overflow-hidden">
......@@ -377,7 +380,7 @@
<div class=" mt-2 mb-1 flex justify-center space-x-2 text-sm font-medium">
<button
class="px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg-lg"
class="px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
on:click={() => {
editMessageConfirmHandler();
}}
......@@ -492,7 +495,7 @@
{/if}
{#if !readOnly}
<Tooltip content="Edit" placement="bottom">
<Tooltip content={$i18n.t('Edit')} placement="bottom">
<button
class="{isLastMessage
? 'visible'
......@@ -519,7 +522,7 @@
</Tooltip>
{/if}
<Tooltip content="Copy" placement="bottom">
<Tooltip content={$i18n.t('Copy')} placement="bottom">
<button
class="{isLastMessage
? 'visible'
......@@ -546,7 +549,7 @@
</Tooltip>
{#if !readOnly}
<Tooltip content="Good Response" placement="bottom">
<Tooltip content={$i18n.t('Good Response')} placement="bottom">
<button
class="{isLastMessage
? 'visible'
......@@ -581,7 +584,7 @@
</button>
</Tooltip>
<Tooltip content="Bad Response" placement="bottom">
<Tooltip content={$i18n.t('Bad Response')} placement="bottom">
<button
class="{isLastMessage
? 'visible'
......@@ -616,7 +619,7 @@
</Tooltip>
{/if}
<Tooltip content="Read Aloud" placement="bottom">
<Tooltip content={$i18n.t('Read Aloud')} placement="bottom">
<button
id="speak-button-{message.id}"
class="{isLastMessage
......@@ -765,7 +768,7 @@
{/if}
{#if message.info}
<Tooltip content="Generation Info" placement="bottom">
<Tooltip content={$i18n.t('Generation Info')} placement="bottom">
<button
class=" {isLastMessage
? 'visible'
......@@ -794,7 +797,7 @@
{/if}
{#if isLastMessage && !readOnly}
<Tooltip content="Continue Response" placement="bottom">
<Tooltip content={$i18n.t('Continue Response')} placement="bottom">
<button
type="button"
class="{isLastMessage
......@@ -826,7 +829,7 @@
</button>
</Tooltip>
<Tooltip content="Regenerate" placement="bottom">
<Tooltip content={$i18n.t('Regenerate')} placement="bottom">
<button
type="button"
class="{isLastMessage
......
......@@ -193,7 +193,7 @@
<div class=" mt-2 mb-1 flex justify-center space-x-2 text-sm font-medium">
<button
id="save-edit-message-button"
class="px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg-lg"
class="px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
on:click={() => {
editMessageConfirmHandler();
}}
......@@ -266,7 +266,7 @@
{/if}
{#if !readOnly}
<Tooltip content="Edit" placement="bottom">
<Tooltip content={$i18n.t('Edit')} placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
on:click={() => {
......@@ -291,7 +291,7 @@
</Tooltip>
{/if}
<Tooltip content="Copy" placement="bottom">
<Tooltip content={$i18n.t('Copy')} placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
......@@ -316,7 +316,7 @@
</Tooltip>
{#if !isFirstMessage && !readOnly}
<Tooltip content="Delete" placement="bottom">
<Tooltip content={$i18n.t('Delete')} placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
......
......@@ -13,6 +13,8 @@
export let selectedModels = [''];
export let disabled = false;
export let showSetDefault = true;
const saveDefaultModel = async () => {
const hasEmptyModel = selectedModels.filter((it) => it === '');
if (hasEmptyModel.length) {
......@@ -38,9 +40,9 @@
<div class="flex flex-col mt-0.5 w-full">
{#each selectedModels as selectedModel, selectedModelIdx}
<div class="flex w-full">
<div class="flex w-full max-w-fit">
<div class="overflow-hidden w-full">
<div class="mr-0.5 max-w-full">
<div class="mr-1 max-w-full">
<Selector
placeholder={$i18n.t('Select a model')}
items={$models
......@@ -57,7 +59,7 @@
{#if selectedModelIdx === 0}
<div class=" self-center mr-2 disabled:text-gray-600 disabled:hover:text-gray-600">
<Tooltip content="Add Model">
<Tooltip content={$i18n.t('Add Model')}>
<button
class=" "
{disabled}
......@@ -69,9 +71,9 @@
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
class="size-3.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
</svg>
......@@ -92,9 +94,9 @@
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
class="size-3.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
</svg>
......@@ -106,6 +108,8 @@
{/each}
</div>
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
</div>
{#if showSetDefault}
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
</div>
{/if}
<script lang="ts">
import { Select } from 'bits-ui';
import { DropdownMenu } from 'bits-ui';
import { flyAndScale } from '$lib/utils/transitions';
import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
......@@ -21,10 +21,15 @@
export let value = '';
export let placeholder = 'Select a model';
export let searchEnabled = true;
export let searchPlaceholder = 'Search a model';
export let searchPlaceholder = $i18n.t('Search a model');
export let items = [{ value: 'mango', label: 'Mango' }];
export let className = 'max-w-lg';
let selectedModel = '';
$: selectedModel = items.find((item) => item.value === value) ?? '';
let searchValue = '';
let ollamaVersion = null;
......@@ -175,27 +180,28 @@
};
</script>
<Select.Root
{items}
<DropdownMenu.Root
onOpenChange={async () => {
searchValue = '';
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
}}
selected={items.find((item) => item.value === value) ?? ''}
onSelectedChange={(selectedItem) => {
value = selectedItem.value;
}}
>
<Select.Trigger class="relative w-full" aria-label={placeholder}>
<Select.Value
class="flex text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
{placeholder}
/>
<ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" />
</Select.Trigger>
<Select.Content
class=" z-40 w-full rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none"
<DropdownMenu.Trigger class="relative w-full" aria-label={placeholder}>
<div
class="flex w-full text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
>
{#if selectedModel}
{selectedModel.label}
{:else}
{placeholder}
{/if}
<ChevronDown className=" self-center ml-2 size-3" strokeWidth="2.5" />
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Content
class=" z-40 w-full {className} justify-start rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none "
transition={flyAndScale}
side={'bottom-start'}
sideOffset={4}
>
<slot>
......@@ -214,12 +220,13 @@
<hr class="border-gray-100 dark:border-gray-800" />
{/if}
<div class="px-3 my-2 max-h-72 overflow-y-auto">
<div class="px-3 my-2 max-h-72 overflow-y-auto scrollbar-none">
{#each filteredItems as item}
<Select.Item
<DropdownMenu.Item
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
value={item.value}
label={item.label}
on:click={() => {
value = item.value;
}}
>
<div class="flex items-center gap-2">
<div class="line-clamp-1">
......@@ -287,7 +294,7 @@
<Check />
</div>
{/if}
</Select.Item>
</DropdownMenu.Item>
{:else}
<div>
<div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100">
......@@ -385,5 +392,16 @@
{/each}
</div>
</slot>
</Select.Content>
</Select.Root>
</DropdownMenu.Content>
</DropdownMenu.Root>
<style>
.scrollbar-none:active::-webkit-scrollbar-thumb,
.scrollbar-none:focus::-webkit-scrollbar-thumb,
.scrollbar-none:hover::-webkit-scrollbar-thumb {
visibility: visible;
}
.scrollbar-none::-webkit-scrollbar-thumb {
visibility: hidden;
}
</style>
......@@ -492,8 +492,8 @@
<input
id="steps-range"
type="range"
min="1"
max="16000"
min="-1"
max="10240000"
step="1"
bind:value={options.num_ctx}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
......@@ -504,9 +504,8 @@
bind:value={options.num_ctx}
type="number"
class=" bg-transparent text-center w-14"
min="1"
max="16000"
step="1"
min="-1"
step="10"
/>
</div>
</div>
......
......@@ -106,6 +106,7 @@
responseAutoCopy = settings.responseAutoCopy ?? false;
showUsername = settings.showUsername ?? false;
fullScreenMode = settings.fullScreenMode ?? false;
splitLargeChunks = settings.splitLargeChunks ?? false;
});
</script>
......
......@@ -35,8 +35,8 @@
</script>
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class="text-gray-700 dark:text-gray-100">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
<div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
<button
class="self-center"
......@@ -56,7 +56,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div
......
......@@ -71,7 +71,7 @@
<Modal bind:show size="sm">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-0.5">
<div class=" text-lg font-medium self-center">{$i18n.t('Share Chat')}</div>
<button
class="self-center"
......@@ -91,10 +91,9 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
{#if chat}
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
<div class="px-5 pt-4 pb-5 w-full flex flex-col justify-center">
<div class=" text-sm dark:text-gray-300 mb-1">
{#if chat.share_id}
<a href="/s/{chat.share_id}" target="_blank"
......
......@@ -8,8 +8,8 @@
</script>
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class="text-gray-700 dark:text-gray-100">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Keyboard shortcuts')}</div>
<button
class="self-center"
......@@ -29,7 +29,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-5 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
......
......@@ -51,7 +51,7 @@
bind:this={modalElement}
class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center z-[9999] overflow-hidden overscroll-contain"
in:fade={{ duration: 10 }}
on:click={() => {
on:mousedown={() => {
show = false;
}}
>
......@@ -60,7 +60,7 @@
size
)} mx-2 bg-gray-50 dark:bg-gray-900 shadow-3xl"
in:flyAndScale
on:click={(e) => {
on:mousedown={(e) => {
e.stopPropagation();
}}
>
......
<script lang="ts">
import TagInput from './Tags/TagInput.svelte';
import TagList from './Tags/TagList.svelte';
import { getContext } from 'svelte';
const i18n = getContext('i18n');
export let tags = [];
......@@ -17,7 +20,7 @@
/>
<TagInput
label={tags.length == 0 ? 'Add Tags' : ''}
label={tags.length == 0 ? $i18n.t('Add Tags') : ''}
on:add={(e) => {
addTag(e.detail);
}}
......
......@@ -96,7 +96,7 @@
<Modal size="sm" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Add Docs')}</div>
<button
class="self-center"
......@@ -116,8 +116,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form
......
......@@ -75,7 +75,7 @@
<Modal size="sm" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Edit Doc')}</div>
<button
class="self-center"
......@@ -95,8 +95,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form
......@@ -111,28 +109,18 @@
<div class="flex flex-1">
<div
class="bg-gray-200 dark:bg-gray-600 font-bold px-3 py-1 border border-r-0 dark:border-gray-600 rounded-l-lg flex items-center"
class="bg-gray-200 dark:bg-gray-800 font-bold px-3 py-0.5 border border-r-0 dark:border-gray-800 rounded-l-xl flex items-center"
>
#
</div>
<input
class="w-full rounded-r-lg py-2.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
class="w-full rounded-r-xl py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="text"
bind:value={doc.name}
autocomplete="off"
required
/>
</div>
<!-- <div class="flex-1">
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="text"
bind:value={doc.name}
autocomplete="off"
required
/>
</div> -->
</div>
<div class="flex flex-col w-full">
......@@ -140,7 +128,7 @@
<div class="flex-1">
<input
class="w-full rounded-lg py-2.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-xl py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
bind:value={doc.title}
autocomplete="off"
......@@ -150,7 +138,7 @@
</div>
<div class="flex flex-col w-full">
<div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
<div class=" mb-2 text-xs text-gray-500">{$i18n.t('Tags')}</div>
<Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
</div>
......
......@@ -137,9 +137,15 @@
if (res) {
console.log('rerankingModelUpdateHandler:', res);
if (res.status === true) {
toast.success($i18n.t('Reranking model set to "{{reranking_model}}"', res), {
duration: 1000 * 10
});
if (rerankingModel === '') {
toast.success($i18n.t('Reranking model disabled', res), {
duration: 1000 * 10
});
} else {
toast.success($i18n.t('Reranking model set to "{{reranking_model}}"', res), {
duration: 1000 * 10
});
}
}
}
};
......@@ -584,12 +590,12 @@
<hr class=" dark:border-gray-700 my-3" />
<div>
<div class=" ">
<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
<div class=" flex">
<div class=" flex w-full justify-between">
<div class="self-center text-xs font-medium flex-1">{$i18n.t('Top K')}</div>
<div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Top K')}</div>
<div class="self-center p-3">
<input
......@@ -602,13 +608,11 @@
/>
</div>
</div>
</div>
{#if querySettings.hybrid === true}
<div class=" flex">
<div class=" flex w-full justify-between">
<div class="self-center text-xs font-medium flex-1">
{$i18n.t('Relevance Threshold')}
{#if querySettings.hybrid === true}
<div class="flex w-full">
<div class=" self-center text-xs font-medium min-w-fit">
{$i18n.t('Minimum Score')}
</div>
<div class="self-center p-3">
......@@ -616,14 +620,25 @@
class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number"
step="0.01"
placeholder={$i18n.t('Enter Relevance Threshold')}
placeholder={$i18n.t('Enter Score')}
bind:value={querySettings.r}
autocomplete="off"
min="0.0"
title={$i18n.t('The score should be a value between 0.0 (0%) and 1.0 (100%).')}
/>
</div>
</div>
{/if}
</div>
{#if querySettings.hybrid === true}
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
{$i18n.t(
'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
)}
</div>
<hr class=" dark:border-gray-700 my-3" />
{/if}
<div>
......@@ -636,8 +651,6 @@
</div>
</div>
<hr class=" dark:border-gray-700 my-3" />
{#if showResetConfirm}
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex items-center space-x-3">
......
......@@ -12,7 +12,7 @@
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Document Settings')}</div>
<button
class="self-center"
......@@ -32,7 +32,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div
......
<script lang="ts">
export let className = 'size-3';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"
/>
</svg>
<script lang="ts">
export let className = 'size-3.5';
export let strokeWidth = '2';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
......@@ -9,6 +9,7 @@
import ModelSelector from '../chat/ModelSelector.svelte';
import Tooltip from '../common/Tooltip.svelte';
import Menu from './Navbar/Menu.svelte';
import { page } from '$app/stores';
const i18n = getContext('i18n');
......@@ -27,22 +28,19 @@
<ShareChatModal bind:show={showShareChatModal} chatId={$chatId} />
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
<div
class=" flex {$settings?.fullScreenMode ?? null ? 'max-w-full' : 'max-w-3xl'}
w-full mx-auto px-3"
>
<div class=" flex max-w-full w-full mx-auto px-5 pt-0.5 md:px-[1.3rem]">
<div class="flex items-center w-full max-w-full">
<div class="flex-1 overflow-hidden max-w-full">
{#if showModelSelector}
<ModelSelector bind:selectedModels />
<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
{/if}
</div>
<div class="self-start flex flex-none items-center">
<div class="flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" />
<div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" />
{#if !shareEnabled}
<Tooltip content="Settings">
<Tooltip content={$i18n.t('Settings')}>
<button
class="cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
id="open-settings-button"
......@@ -104,7 +102,7 @@
</button>
</Menu>
{/if}
<Tooltip content="New Chat">
<Tooltip content={$i18n.t('New Chat')}>
<button
id="new-chat-button"
class=" cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
......
<script lang="ts">
import { DropdownMenu } from 'bits-ui';
import { getContext } from 'svelte';
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
......@@ -12,6 +13,8 @@
import { downloadChatAsPDF } from '$lib/apis/utils';
const i18n = getContext('i18n');
export let shareEnabled: boolean = false;
export let shareHandler: Function;
export let downloadHandler: Function;
......@@ -104,7 +107,7 @@
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
/>
</svg>
<div class="flex items-center">Settings</div>
<div class="flex items-center">{$i18n.t('Settings')}</div>
</DropdownMenu.Item>
{#if shareEnabled}
......@@ -126,7 +129,7 @@
clip-rule="evenodd"
/>
</svg>
<div class="flex items-center">Share</div>
<div class="flex items-center">{$i18n.t('Share')}</div>
</DropdownMenu.Item>
<!-- <DropdownMenu.Item
......@@ -154,7 +157,7 @@
/>
</svg>
<div class="flex items-center">Download</div>
<div class="flex items-center">{$i18n.t('Download')}</div>
</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent
class="w-full rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-900 dark:text-white shadow-lg"
......@@ -167,7 +170,7 @@
downloadTxt();
}}
>
<div class="flex items-center line-clamp-1">Plain text (.txt)</div>
<div class="flex items-center line-clamp-1">{$i18n.t('Plain text (.txt)')}</div>
</DropdownMenu.Item>
<DropdownMenu.Item
......@@ -176,7 +179,7 @@
downloadPdf();
}}
>
<div class="flex items-center line-clamp-1">PDF document (.pdf)</div>
<div class="flex items-center line-clamp-1">{$i18n.t('PDF document (.pdf)')}</div>
</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
......
<script lang="ts">
import { v4 as uuidv4 } from 'uuid';
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/stores';
import { user, chats, settings, showSettings, chatId, tags } from '$lib/stores';
import { goto } from '$app/navigation';
import { user, chats, settings, showSettings, chatId, tags, showSidebar } from '$lib/stores';
import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
......@@ -30,6 +24,7 @@
import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
const BREAKPOINT = 1024;
let show = false;
let navElement;
......@@ -50,48 +45,49 @@
let isEditing = false;
onMount(async () => {
show = window.innerWidth > BREAKPOINT;
showSidebar.set(window.innerWidth > BREAKPOINT);
await chats.set(await getChatList(localStorage.token));
let touchstartX = 0;
let touchendX = 0;
let touchstart;
let touchend;
function checkDirection() {
const screenWidth = window.innerWidth;
const swipeDistance = Math.abs(touchendX - touchstartX);
if (swipeDistance >= screenWidth / 4) {
if (touchendX < touchstartX) {
show = false;
const swipeDistance = Math.abs(touchend.screenX - touchstart.screenX);
if (touchstart.clientX < 40 && swipeDistance >= screenWidth / 8) {
if (touchend.screenX < touchstart.screenX) {
showSidebar.set(false);
}
if (touchendX > touchstartX) {
show = true;
if (touchend.screenX > touchstart.screenX) {
showSidebar.set(true);
}
}
}
const onTouchStart = (e) => {
touchstartX = e.changedTouches[0].screenX;
touchstart = e.changedTouches[0];
console.log(touchstart.clientX);
};
const onTouchEnd = (e) => {
touchendX = e.changedTouches[0].screenX;
touchend = e.changedTouches[0];
checkDirection();
};
const onResize = () => {
if (show && window.innerWidth < BREAKPOINT) {
show = false;
if ($showSidebar && window.innerWidth < BREAKPOINT) {
showSidebar.set(false);
}
};
document.addEventListener('touchstart', onTouchStart);
document.addEventListener('touchend', onTouchEnd);
window.addEventListener('touchstart', onTouchStart);
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('resize', onResize);
return () => {
document.removeEventListener('touchstart', onTouchStart);
document.removeEventListener('touchend', onTouchEnd);
document.removeEventListener('resize', onResize);
window.removeEventListener('touchstart', onTouchStart);
window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('resize', onResize);
};
});
......@@ -166,13 +162,15 @@
<div
bind:this={navElement}
class="h-screen max-h-[100dvh] min-h-screen {show
id="sidebar"
class="h-screen max-h-[100dvh] min-h-screen {$showSidebar
? 'lg:relative w-[260px]'
: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0
: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0 rounded-r-2xl
"
data-state={$showSidebar}
>
<div
class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] {show
class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] {$showSidebar
? ''
: 'invisible'}"
>
......@@ -419,7 +417,7 @@
</div>
{/if}
<div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto">
<div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto scrollbar-none">
{#each $chats.filter((chat) => {
if (search === '') {
return true;
......@@ -465,7 +463,7 @@
on:click={() => {
selectedChatId = chat.id;
if (window.innerWidth < 1024) {
show = false;
showSidebar.set(false);
}
}}
draggable="false"
......@@ -610,7 +608,7 @@
</button>
</ChatMenu>
<Tooltip content="Archive">
<Tooltip content={$i18n.t('Archive')}>
<button
aria-label="Archive"
class=" self-center dark:hover:text-white transition"
......@@ -621,6 +619,27 @@
<ArchiveBox />
</button>
</Tooltip>
{#if chat.id === $chatId}
<button
id="delete-chat-button"
class="hidden"
on:click={() => {
chatDeleteId = chat.id;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
/>
</svg>
</button>
{/if}
</div>
{/if}
</div>
......@@ -802,14 +821,14 @@
>
<Tooltip
placement="right"
content={`${show ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
content={`${$showSidebar ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
touch={false}
>
<button
id="sidebar-toggle-button"
class=" group"
on:click={() => {
show = !show;
showSidebar.set(!$showSidebar);
}}
><span class="" data-state="closed"
><div
......@@ -833,3 +852,14 @@
</Tooltip>
</div>
</div>
<style>
.scrollbar-none:active::-webkit-scrollbar-thumb,
.scrollbar-none:focus::-webkit-scrollbar-thumb,
.scrollbar-none:hover::-webkit-scrollbar-thumb {
visibility: visible;
}
.scrollbar-none::-webkit-scrollbar-thumb {
visibility: hidden;
}
</style>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment