153 lines
4.5 KiB
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}
|