ai-ui/vite/src/ChatMessage.svelte

153 lines
4.5 KiB
Svelte

<script>
import { beforeUpdate, afterUpdate, createEventDispatcher} from 'svelte';
import { copyToClipboard } from './lib/clipboard.js';
import {Tooltip} from 'bootstrap';
export let message;
export let container;
export let markdown;
export let message_index;
const dispatch = createEventDispatcher();
let autoscroll;
let renderedContent = '';
let editableElement;
// Track whether the user is editing the message
let isEditing = false;
$: renderedContent = markdown.render(message.content);
beforeUpdate(() => {
autoscroll = container && container.offsetHeight + container.scrollTop >
container.scrollHeight - 20;
});
afterUpdate(() => {
if (autoscroll) container.scrollTo(0, container.scrollHeight);
if (isEditing && editableElement) {
editableElement.focus();
}
});
function handleClick(event) {
let codeToCopy = null;
// Vérifiez si l'élément cliqué ou son parent immédiat est le bouton de copie
const target = event.target;
const isCopyButton = target.classList.contains('hljs-copy-button');
const isChildOfCopyButton = target.parentNode && target.parentNode.classList.contains('hljs-copy-button');
if (isCopyButton || isChildOfCopyButton) {
// Trouvez le bon élément parent pour accéder au texte à copier
const copyButton = isCopyButton ? target : target.parentNode;
codeToCopy = copyButton.parentNode.nextElementSibling.innerText;
copyToClipboard(codeToCopy).then(() => {
let tooltip = new Tooltip(copyButton);
tooltip.setContent({ '.tooltip-inner': 'Code copié!' });
tooltip.show();
// Destroy tooltip after 2 seconds
setTimeout(() => {
tooltip.dispose();
}, 2000);
});
event.stopPropagation();
}
}
function handleKeyDown(event) {
if (event.key === 'Enter' && event.shiftKey) {
// Allow the default behavior of adding a new line
return;
}
switch (event.key) {
case 'Escape':
isEditing = false;
break;
case 'Enter':
event.preventDefault();
const editedContent = event.target.innerText.trim();
if (editedContent) {
dispatch('editMessage', {
index: message_index,
content: editedContent
});
}
isEditing = false;
break;
}
}
function handleCopy(event) {
const btn = event.currentTarget;
event.stopPropagation();
copyToClipboard(message.content)
.then(() => {
let tooltip = new Tooltip(btn);
tooltip.setContent({ '.tooltip-inner': 'Message copié!' });
tooltip.show();
// Destroy tooltip after 2 seconds
setTimeout(() => {
tooltip.dispose();
}, 2000);
})
.catch(err => console.log(`Failed to copy clipboard: ${err.message}`));
}
</script>
<style>
.message {
padding: 16px;
}
.assistant {
background-color: #f0f0f0;
}
</style>
{#if message.role != 'system'}
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
<div class={message.role + ' message'} on:click={handleClick}>
{#if isEditing}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div bind:this={editableElement}
class="editable-message"
contenteditable="true"
on:keydown={handleKeyDown}>{message.content}</div>
{:else}
{@html renderedContent}
<div class="toolbar">
{#if message.role == 'user'}
<button class="btn btn-primary btn-sm" title="Modifier" on:click={() => {isEditing = true}}>
<span class="icon-edit"></span>
</button>
{:else if message.role == 'assistant'}
<button class="btn btn-primary btn-sm" title="Copier" on:click={handleCopy}>
<span class="icon-content_copy"></span>
</button>
<button class="btn btn-primary btn-sm" title="Régénérer">
<span class="icon-loop"></span>
</button>
<button class="btn btn-primary btn-sm" title="Supprimer le message">
<span class="icon-delete"></span>
</button>
{/if}
</div>
{/if}
</div>
{/if}