Add truncate and send button to messages
This commit is contained in:
parent
e2b9c57164
commit
58c78a2840
|
@ -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)
|
||||||
|
|
|
@ -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 = () => {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue