open-webui/src/lib/components/channel/Messages/Message.svelte

192 lines
5.4 KiB
Svelte
Raw Normal View History

2024-12-23 02:40:01 +00:00
<script lang="ts">
2024-12-23 04:56:51 +00:00
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import isToday from 'dayjs/plugin/isToday';
import isYesterday from 'dayjs/plugin/isYesterday';
dayjs.extend(relativeTime);
dayjs.extend(isToday);
dayjs.extend(isYesterday);
import { getContext } from 'svelte';
const i18n = getContext<Writable<i18nType>>('i18n');
import { settings } from '$lib/stores';
import { WEBUI_BASE_URL } from '$lib/constants';
2024-12-23 02:40:01 +00:00
import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
2024-12-23 04:56:51 +00:00
import ProfileImage from '$lib/components/chat/Messages/ProfileImage.svelte';
import Name from '$lib/components/chat/Messages/Name.svelte';
2024-12-23 07:53:45 +00:00
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
import Pencil from '$lib/components/icons/Pencil.svelte';
2024-12-23 02:40:01 +00:00
export let message;
2024-12-23 04:56:51 +00:00
export let showUserProfile = true;
2024-12-23 07:53:45 +00:00
export let onDelete: Function = () => {};
export let onEdit: Function = () => {};
let edit = false;
let editedContent = null;
let showDeleteConfirmDialog = false;
2024-12-23 04:56:51 +00:00
const formatDate = (inputDate) => {
const date = dayjs(inputDate);
const now = dayjs();
if (date.isToday()) {
return `Today at ${date.format('HH:mm')}`;
} else if (date.isYesterday()) {
return `Yesterday at ${date.format('HH:mm')}`;
} else {
return `${date.format('DD/MM/YYYY')} at ${date.format('HH:mm')}`;
}
};
2024-12-23 02:40:01 +00:00
</script>
2024-12-23 07:53:45 +00:00
<ConfirmDialog
bind:show={showDeleteConfirmDialog}
title={$i18n.t('Delete Message')}
message={$i18n.t('Are you sure you want to delete this message?')}
onConfirm={async () => {
await onDelete(message.id);
}}
/>
2024-12-23 02:40:01 +00:00
{#if message}
2024-12-23 04:56:51 +00:00
<div
class="flex flex-col justify-between px-5 {showUserProfile
2024-12-23 07:41:22 +00:00
? 'pt-1.5 pb-0.5'
2024-12-23 04:56:51 +00:00
: ''} w-full {($settings?.widescreenMode ?? null)
? 'max-w-full'
2024-12-23 07:53:45 +00:00
: 'max-w-5xl'} mx-auto group hover:bg-gray-500/5 transition relative"
2024-12-23 04:56:51 +00:00
>
2024-12-23 07:53:45 +00:00
<div class=" absolute invisible group-hover:visible right-1 -top-2">
<div
class="flex gap-1 rounded-lg bg-white dark:bg-gray-850 shadow-md p-0.5 border border-gray-100 dark:border-gray-800"
>
<button
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
on:click={() => {
edit = true;
editedContent = message.content;
}}
>
<Pencil />
</button>
<button
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
on:click={() => (showDeleteConfirmDialog = true)}
>
<GarbageBin />
</button>
</div>
</div>
2024-12-23 04:56:51 +00:00
<div
class=" flex w-full message-{message.id}"
id="message-{message.id}"
dir={$settings.chatDirection}
>
<div
class={`flex-shrink-0 ${($settings?.chatDirection ?? 'LTR') === 'LTR' ? 'mr-3' : 'ml-3'}`}
>
{#if showUserProfile}
<ProfileImage
src={message.user?.profile_image_url ??
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
2024-12-23 07:41:22 +00:00
className={'size-8 translate-y-1 mr-0.5'}
2024-12-23 04:56:51 +00:00
/>
{:else}
2024-12-23 07:41:22 +00:00
<!-- <div class="w-7 h-7 rounded-full bg-transparent" /> -->
{#if message.created_at}
<span
class=" text-xs self-center invisible group-hover:visible text-gray-500 font-medium first-letter:capitalize"
>
{dayjs(message.created_at / 1000000).format('HH:mm')}
</span>
{/if}
2024-12-23 04:56:51 +00:00
{/if}
</div>
<div class="flex-auto w-0 pl-1">
{#if showUserProfile}
<Name>
2024-12-23 07:41:22 +00:00
<span class="text-sm">
{message?.user?.name}
</span>
2024-12-23 04:56:51 +00:00
{#if message.created_at}
<span
2024-12-23 06:09:51 +00:00
class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium first-letter:capitalize ml-0.5 -mt-0.5"
2024-12-23 04:56:51 +00:00
>
{formatDate(message.created_at / 1000000)}
</span>
{/if}
</Name>
{/if}
2024-12-23 07:53:45 +00:00
{#if edit}
<div class="py-1">
<textarea
id="message-edit-{message.id}"
class=" bg-transparent outline-none w-full resize-none"
bind:value={editedContent}
on:input={(e) => {
e.target.style.height = '';
e.target.style.height = `${e.target.scrollHeight}px`;
}}
on:keydown={(e) => {
if (e.key === 'Escape') {
document.getElementById('close-edit-message-button')?.click();
}
const isCmdOrCtrlPressed = e.metaKey || e.ctrlKey;
const isEnterPressed = e.key === 'Enter';
if (isCmdOrCtrlPressed && isEnterPressed) {
document.getElementById('confirm-edit-message-button')?.click();
}
}}
/>
<div class=" mt-2 mb-1 flex justify-end text-sm font-medium">
<div class="flex space-x-1.5">
<button
id="close-edit-message-button"
class="px-4 py-2 bg-white dark:bg-gray-900 hover:bg-gray-100 text-gray-800 dark:text-gray-100 transition rounded-3xl"
on:click={() => {
edit = false;
editedContent = null;
}}
>
{$i18n.t('Cancel')}
</button>
<button
id="confirm-edit-message-button"
class=" px-4 py-2 bg-gray-900 dark:bg-white hover:bg-gray-850 text-gray-100 dark:text-gray-800 transition rounded-3xl"
on:click={async () => {
onEdit(message.id, editedContent);
edit = false;
editedContent = null;
}}
>
{$i18n.t('Save')}
</button>
</div>
</div>
</div>
{:else}
<div class="markdown-prose">
<Markdown id={message.id} content={message.content} />
</div>
{/if}
2024-12-23 04:56:51 +00:00
</div>
2024-12-23 02:40:01 +00:00
</div>
</div>
{/if}