mirror of
https://github.com/morgan9e/chatgpt-web
synced 2026-04-14 00:14:04 +09:00
Refactor and add many features
This commit is contained in:
192
src/lib/EditMessage.svelte
Normal file
192
src/lib/EditMessage.svelte
Normal file
@@ -0,0 +1,192 @@
|
||||
<script lang="ts">
|
||||
import Code from './Code.svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { deleteMessage, getChatSettingValueByKey } from './Storage.svelte'
|
||||
import { getPrice } from './Stats.svelte'
|
||||
import SvelteMarkdown from 'svelte-markdown'
|
||||
import type { Message, Model } from './Types.svelte'
|
||||
import Fa from 'svelte-fa/src/fa.svelte'
|
||||
import { faTrash, faDiagramPredecessor, faDiagramNext } from '@fortawesome/free-solid-svg-icons/index'
|
||||
|
||||
export let message:Message
|
||||
export let chatId:number
|
||||
|
||||
// Marked options
|
||||
const markedownOptions = {
|
||||
gfm: true, // Use GitHub Flavored Markdown
|
||||
breaks: true, // Enable line breaks in markdown
|
||||
mangle: false // Do not mangle email addresses
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let editing = false
|
||||
let original
|
||||
let defaultModel:Model
|
||||
let noEdit
|
||||
|
||||
onMount(() => {
|
||||
original = message.content
|
||||
defaultModel = getChatSettingValueByKey(chatId, 'model')
|
||||
noEdit = message.summarized
|
||||
})
|
||||
|
||||
function edit (msgid) {
|
||||
if (noEdit) return
|
||||
editing = true
|
||||
setTimeout(() => {
|
||||
const el = document.getElementById(msgid)
|
||||
el && el.focus()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
let dbnc
|
||||
function update () {
|
||||
clearTimeout(dbnc)
|
||||
dbnc = setTimeout(() => { doChange() }, 250)
|
||||
}
|
||||
|
||||
function doChange () {
|
||||
if (message.content !== original) {
|
||||
dispatch('change', message)
|
||||
}
|
||||
}
|
||||
|
||||
function exit () {
|
||||
doChange()
|
||||
editing = false
|
||||
}
|
||||
|
||||
function keydown (event) {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault()
|
||||
message.content = original
|
||||
editing = false
|
||||
}
|
||||
}
|
||||
const scrollToMessage = (uuid:string | string[] | undefined) => {
|
||||
if (Array.isArray(uuid)) {
|
||||
uuid = uuid[0]
|
||||
}
|
||||
if (!uuid) {
|
||||
console.error('Not a valid uuid', uuid)
|
||||
return
|
||||
}
|
||||
const el = document.getElementById('message-' + uuid)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
} else {
|
||||
console.error("Can't find element with message ID", uuid)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key message.uuid}
|
||||
<article
|
||||
id="{'message-' + message.uuid}"
|
||||
class="message"
|
||||
class:is-info={message.role === 'user'}
|
||||
class:is-success={message.role === 'assistant'}
|
||||
class:is-warning={message.role === 'system'}
|
||||
class:is-danger={message.role === 'error'}
|
||||
class:user-message={message.role === 'user' || message.role === 'system'}
|
||||
class:assistant-message={message.role === 'error' || message.role === 'assistant'}
|
||||
class:summarized={message.summarized}
|
||||
>
|
||||
<div class="message-body content">
|
||||
<div class="greyscale is-pulled-right ml-2 button-pack">
|
||||
{#if !message.summarized && !message.summary}
|
||||
<a
|
||||
href={'#'}
|
||||
class=" delButton"
|
||||
on:click|preventDefault={() => {
|
||||
// messages.splice(i, 1)
|
||||
deleteMessage(chatId, message.uuid)
|
||||
}}
|
||||
>
|
||||
<Fa icon={faTrash} />
|
||||
</a>
|
||||
{:else if message.summarized}
|
||||
<a
|
||||
href={'#'}
|
||||
class="delButton"
|
||||
on:click|preventDefault={() => {
|
||||
scrollToMessage(message.summarized)
|
||||
}}
|
||||
>
|
||||
<Fa icon={faDiagramNext} />
|
||||
</a>
|
||||
{/if}
|
||||
{#if message.summary}
|
||||
<a
|
||||
href={'#'}
|
||||
class="delButton"
|
||||
on:click|preventDefault={() => {
|
||||
scrollToMessage(message.summary)
|
||||
}}
|
||||
>
|
||||
<Fa icon={faDiagramPredecessor} />
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{#if editing && !noEdit}
|
||||
<form class="message-edit" on:submit|preventDefault={update} on:keydown={keydown}>
|
||||
<div id={'edit-' + message.uuid} class="message-editor" bind:innerText={message.content} contenteditable on:input={update} on:blur={exit} />
|
||||
</form>
|
||||
{:else}
|
||||
<a href={'#'} class="message-display" on:click|preventDefault={() => {}} on:dblclick|preventDefault={() => edit('edit-' + message.uuid)}>
|
||||
<SvelteMarkdown
|
||||
source={message.content}
|
||||
options={markedownOptions}
|
||||
renderers={{ code: Code, html: Code }}
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
{#if message.role === 'system'}
|
||||
<p class="is-size-7">System Prompt</p>
|
||||
{:else if message.usage}
|
||||
<p class="is-size-7">
|
||||
This message was generated on <em>{message.model || defaultModel}</em> using <span class="has-text-weight-bold">{message.usage.total_tokens}</span>
|
||||
tokens ~= <span class="has-text-weight-bold">${getPrice(message.usage, message.model || defaultModel).toFixed(6)}</span>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</article>
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
.message-edit {
|
||||
display: block;
|
||||
}
|
||||
.message-editor {
|
||||
white-space: pre-wrap;
|
||||
min-width: 100px;
|
||||
min-height: 30px;
|
||||
}
|
||||
a.message-display {
|
||||
display: block;
|
||||
text-decoration: none !important;
|
||||
min-width: 100px;
|
||||
min-height: 30px;
|
||||
}
|
||||
.button-pack {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 2px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.assistant-message .button-pack {
|
||||
right: auto;
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
}
|
||||
.message {
|
||||
position: relative;
|
||||
}
|
||||
.message:hover .button-pack, .message:focus .button-pack {
|
||||
display: block;
|
||||
}
|
||||
.summarized {
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user