Add truncate and send button to messages

This commit is contained in:
Webifi 2023-05-30 00:31:31 -05:00
parent e2b9c57164
commit 58c78a2840
5 changed files with 127 additions and 106 deletions

View File

@ -10,7 +10,8 @@
updateChatSettings, updateChatSettings,
updateRunningTotal, updateRunningTotal,
checkStateChange, checkStateChange,
showSetChatSettings showSetChatSettings,
submitExitingPromptsNow
} from './Storage.svelte' } from './Storage.svelte'
import { getRequestSettingList, defaultModel } from './Settings.svelte' import { getRequestSettingList, defaultModel } from './Settings.svelte'
import { import {
@ -75,10 +76,14 @@
$showSetChatSettings = false $showSetChatSettings = false
showSettingsModal() showSettingsModal()
} }
if ($submitExitingPromptsNow) {
$submitExitingPromptsNow = false
submitForm(false, true)
}
}) })
} }
$: onStateChange($checkStateChange, $showSetChatSettings) $: onStateChange($checkStateChange, $showSetChatSettings, $submitExitingPromptsNow)
// Make sure chat object is ready to go // Make sure chat object is ready to go
updateChatSettings(chatId) updateChatSettings(chatId)

View File

@ -54,11 +54,6 @@
showProfileMenu = false showProfileMenu = false
$checkStateChange++ $checkStateChange++
showSettingsModal = 0 showSettingsModal = 0
// if (chat.startSession) {
// chat.startSession = false
// saveChatStore()
// // submitForm(false, true)
// }
} }
const clearSettings = () => { const clearSettings = () => {

View File

@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import Code from './Code.svelte' import Code from './Code.svelte'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import { deleteMessage, chatsStorage, deleteSummaryMessage } from './Storage.svelte' import { deleteMessage, chatsStorage, deleteSummaryMessage, truncateFromMessage, submitExitingPromptsNow } from './Storage.svelte'
import { getPrice } from './Stats.svelte' import { getPrice } from './Stats.svelte'
import SvelteMarkdown from 'svelte-markdown' import SvelteMarkdown from 'svelte-markdown'
import type { Message, Model, Chat } from './Types.svelte' import type { Message, Model, Chat } from './Types.svelte'
import Fa from 'svelte-fa/src/fa.svelte' import Fa from 'svelte-fa/src/fa.svelte'
import { faTrash, faDiagramPredecessor, faDiagramNext } from '@fortawesome/free-solid-svg-icons/index' import { faTrash, faDiagramPredecessor, faDiagramNext, faCircleCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons/index'
import { scrollIntoViewWithOffset } from './Util.svelte' import { scrollIntoViewWithOffset } from './Util.svelte'
export let message:Message export let message:Message
@ -44,30 +44,6 @@
}, 0) }, 0)
} }
const checkDelete = () => {
if (message.summarized) {
// is in a summary, so we're summarized
window.alert('Sorry, you can\'t delete a summarized message')
return
}
if (message.summary) {
// We're linked to messages we're a summary of
if (window.confirm('Are you sure you want to delete this summary?\nYour session may be too long to submit again after you do.')) {
try {
deleteSummaryMessage(chatId, message.uuid)
} catch (e) {
window.alert('Unable to delete summary:\n' + e.message)
}
}
return
}
try {
deleteMessage(chatId, message.uuid)
} catch (e) {
window.alert('Unable to delete:\n' + e.message)
}
}
let dbnc let dbnc
const update = () => { const update = () => {
clearTimeout(dbnc) clearTimeout(dbnc)
@ -103,7 +79,7 @@
} }
const el = document.getElementById('message-' + uuid) const el = document.getElementById('message-' + uuid)
if (el) { if (el) {
scrollIntoViewWithOffset(el, 60) scrollIntoViewWithOffset(el, 80)
} else { } else {
console.error("Can't find element with message ID", uuid) console.error("Can't find element with message ID", uuid)
} }
@ -120,6 +96,65 @@
lastTap = new Date().getTime() lastTap = new Date().getTime()
} }
let waitingForDeleteConfirm:any = 0
const checkDelete = () => {
clearTimeout(waitingForTruncateConfirm); waitingForTruncateConfirm = 0
if (!waitingForDeleteConfirm) {
// wait a second for another click to avoid accidental deletes
waitingForDeleteConfirm = setTimeout(() => { waitingForDeleteConfirm = 0 }, 1000)
return
}
clearTimeout(waitingForDeleteConfirm)
waitingForDeleteConfirm = 0
if (message.summarized) {
// is in a summary, so we're summarized
window.alert('Sorry, you can\'t delete a summarized message')
return
}
if (message.summary) {
// We're linked to messages we're a summary of
if (window.confirm('Are you sure you want to delete this summary?\nYour session may be too long to submit again after you do.')) {
try {
deleteSummaryMessage(chatId, message.uuid)
} catch (e) {
window.alert('Unable to delete summary:\n' + e.message)
}
}
return
}
try {
deleteMessage(chatId, message.uuid)
} catch (e) {
window.alert('Unable to delete:\n' + e.message)
}
}
let waitingForTruncateConfirm:any = 0
const checkTruncate = () => {
clearTimeout(waitingForDeleteConfirm); waitingForDeleteConfirm = 0
if (!waitingForTruncateConfirm) {
// wait a second for another click to avoid accidental deletes
waitingForTruncateConfirm = setTimeout(() => { waitingForTruncateConfirm = 0 }, 1000)
return
}
clearTimeout(waitingForTruncateConfirm)
waitingForTruncateConfirm = 0
if (message.summarized) {
// is in a summary, so we're summarized
window.alert('Sorry, you can\'t truncate a summarized message')
return
}
try {
truncateFromMessage(chatId, message.uuid)
$submitExitingPromptsNow = true
} catch (e) {
window.alert('Unable to delete:\n' + e.message)
}
}
</script> </script>
{#key message.uuid} {#key message.uuid}
@ -136,39 +171,61 @@
class:editing={editing} class:editing={editing}
> >
<div class="message-body content"> <div class="message-body content">
<div class="greyscale is-pulled-right ml-2 button-pack"> <div class="button-pack">
{#if message.summarized} {#if message.summarized}
<a <a
href={'#'} href={'#'}
class="msg-summary-button" title="Jump to summary"
class="msg-summary-button button is-small is-info"
on:click|preventDefault={() => { on:click|preventDefault={() => {
scrollToMessage(message.summarized) scrollToMessage(message.summarized)
}} }}
> >
<Fa icon={faDiagramNext} /> <span class="icon"><Fa icon={faDiagramNext} /></span>
</a> </a>
{/if} {/if}
{#if message.summary} {#if message.summary}
<a <a
href={'#'} href={'#'}
class="msg-summarized-button" title="Jump to summarized"
class="msg-summarized-button button is-small is-info"
on:click|preventDefault={() => { on:click|preventDefault={() => {
scrollToMessage(message.summary) scrollToMessage(message.summary)
}} }}
> >
<Fa icon={faDiagramPredecessor} /> <span class="icon"><Fa icon={faDiagramPredecessor} /></span>
</a> </a>
{/if} {/if}
{#if !message.summarized} {#if !message.summarized}
<a <a
href={'#'} href={'#'}
class=" msg-delete-button" title="Delete this message"
class=" msg-delete-button button is-small is-warning"
on:click|preventDefault={() => { on:click|preventDefault={() => {
// messages.splice(i, 1)
checkDelete() checkDelete()
}} }}
> >
<Fa icon={faTrash} /> {#if waitingForDeleteConfirm}
<span class="icon"><Fa icon={faCircleCheck} /></span>
{:else}
<span class="icon"><Fa icon={faTrash} /></span>
{/if}
</a>
{/if}
{#if !message.summarized}
<a
href={'#'}
title="Truncate all and submit"
class=" msg-delete-button button is-small is-danger"
on:click|preventDefault={() => {
checkTruncate()
}}
>
{#if waitingForTruncateConfirm}
<span class="icon"><Fa icon={faCircleCheck} /></span>
{:else}
<span class="icon"><Fa icon={faPaperPlane} /></span>
{/if}
</a> </a>
{/if} {/if}
</div> </div>
@ -216,17 +273,25 @@
min-width: 100px; min-width: 100px;
min-height: 30px; min-height: 30px;
} }
.button-pack .button {
display: block;
margin: 4px;
border-radius: 10px;
opacity: .8;
}
.button-pack .button:hover {
opacity: 1;
}
.button-pack { .button-pack {
display: none; display: none;
position: absolute; position: absolute;
right: 10px; right: -20px;
top: 2px; top: -20px;
text-decoration: none; text-decoration: none;
} }
.assistant-message .button-pack { .assistant-message .button-pack {
right: auto; right: auto;
left: 5px; left: -20px;
top: 2px;
} }
.message { .message {
position: relative; position: relative;

View File

@ -9,17 +9,6 @@
$: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined $: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined
// let fileinput
// const onFileSelected = (e) => {
// const image = e.target.files[0]
// const reader = new FileReader()
// reader.readAsText(image)
// reader.onload = e => {
// const json = (e.target || {}).result as string
// addChatFromJSON(json)
// }
// }
</script> </script>
<aside class="menu"> <aside class="menu">
@ -48,53 +37,5 @@
> >
</li> </li>
{/if} {/if}
<!-- <li>
<a class="panel-block"
href="{'#/'}"
class:is-disabled={!$apiKeyStorage}
on:click|preventDefault={() => {
const confirmDelete = window.confirm('Are you sure you want to delete all your chats?')
if (confirmDelete) {
replace('#/').then(() => clearChats())
}
}}><span class="greyscale mr-2"><Fa icon={faTrash} /></span> Clear chats</a
>
</li>
{#if activeChatId}
<li>
<a
href={'#/'}
class="panel-block"
class:is-disabled={!apiKeyStorage}
on:click|preventDefault={() => {
if (activeChatId) {
exportAsMarkdown(activeChatId)
}
}}><span class="greyscale mr-2"><Fa icon={faFileExport} /></span> Export chat</a
>
</li>
<li>
<a
href={'#/'}
class="panel-block"
class:is-disabled={!apiKeyStorage}
on:click|preventDefault={() => {
if (activeChatId) {
exportChatAsJSON(activeChatId)
}
}}><span class="greyscale mr-2"><Fa icon={faDownload} /></span> Save chat</a
>
</li>
{/if} -->
<!-- <li>
<a
href={'#/'}
class="panel-block"
class:is-disabled={!apiKeyStorage}
on:click|preventDefault={() => { fileinput.click() }}><span class="greyscale mr-2"><Fa icon={faUpload} /></span> Load chat</a
>
<input style="display:none" type="file" accept=".json" on:change={(e) => onFileSelected(e)} bind:this={fileinput} >
</li> -->
</ul> </ul>
</aside> </aside>

View File

@ -11,6 +11,7 @@
export const apiKeyStorage = persisted('apiKey', '' as string) export const apiKeyStorage = persisted('apiKey', '' as string)
export let checkStateChange = writable(0) // Trigger for Chat export let checkStateChange = writable(0) // Trigger for Chat
export let showSetChatSettings = writable(false) // export let showSetChatSettings = writable(false) //
export let submitExitingPromptsNow = writable(false) // for them to go now. Will not submit anything in the input
const chatDefaults = getChatDefaults() const chatDefaults = getChatDefaults()
@ -219,6 +220,20 @@
chatsStorage.set(chats) chatsStorage.set(chats)
} }
export const truncateFromMessage = (chatId: number, uuid: string) => {
const chats = get(chatsStorage)
const chat = chats.find((chat) => chat.id === chatId) as Chat
const index = chat.messages.findIndex((m) => m.uuid === uuid)
const message = getMessage(chat, uuid)
if (message && message.summarized) throw new Error('Unable to truncate from a summarized message')
// const found = chat.messages.filter((m) => m.uuid === uuid)
if (index < 0) {
throw new Error(`Unable to find message with ID: ${uuid}`)
}
chat.messages.splice(index + 1) // remove every item after
chatsStorage.set(chats)
}
export const clearMessages = (chatId: number) => { export const clearMessages = (chatId: number) => {
const chats = get(chatsStorage) const chats = get(chatsStorage)
const chat = chats.find((chat) => chat.id === chatId) as Chat const chat = chats.find((chat) => chat.id === chatId) as Chat