mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-05 13:10:24 +00:00
* lint: turn on type import rules setting up for verbatimModuleSyntax * lint: --fix for type imports
135 lines
4.1 KiB
Vue
135 lines
4.1 KiB
Vue
<template>
|
|
<ScrollPanel
|
|
ref="scrollPanelRef"
|
|
class="w-full min-h-[400px] rounded-lg px-2 py-2 text-xs"
|
|
:pt="{ content: { id: 'chat-scroll-content' } }"
|
|
>
|
|
<div v-for="(item, i) in parsedHistory" :key="i" class="mb-4">
|
|
<!-- Prompt (user, right) -->
|
|
<span
|
|
:class="{
|
|
'opacity-40 pointer-events-none': editIndex !== null && i > editIndex
|
|
}"
|
|
>
|
|
<div class="flex justify-end mb-1">
|
|
<div
|
|
class="bg-gray-300 dark-theme:bg-gray-800 rounded-xl px-4 py-1 max-w-[80%] text-right"
|
|
>
|
|
<div class="break-words text-[12px]">{{ item.prompt }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-end mb-2 mr-1">
|
|
<CopyButton :text="item.prompt" />
|
|
<Button
|
|
v-tooltip="
|
|
editIndex === i ? $t('chatHistory.cancelEditTooltip') : null
|
|
"
|
|
text
|
|
rounded
|
|
class="p-1! h-4! w-4! text-gray-400 hover:text-gray-600 hover:dark-theme:text-gray-200 transition"
|
|
pt:icon:class="text-xs!"
|
|
:icon="editIndex === i ? 'pi pi-times' : 'pi pi-pencil'"
|
|
:aria-label="
|
|
editIndex === i ? $t('chatHistory.cancelEdit') : $t('g.edit')
|
|
"
|
|
@click="editIndex === i ? handleCancelEdit() : handleEdit(i)"
|
|
/>
|
|
</div>
|
|
</span>
|
|
<!-- Response (LLM, left) -->
|
|
<ResponseBlurb
|
|
:text="item.response"
|
|
:class="{
|
|
'opacity-25 pointer-events-none': editIndex !== null && i >= editIndex
|
|
}"
|
|
>
|
|
<div v-html="nl2br(linkifyHtml(item.response))" />
|
|
</ResponseBlurb>
|
|
</div>
|
|
</ScrollPanel>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import Button from 'primevue/button'
|
|
import ScrollPanel from 'primevue/scrollpanel'
|
|
import { computed, nextTick, ref, watch } from 'vue'
|
|
|
|
import CopyButton from '@/components/graph/widgets/chatHistory/CopyButton.vue'
|
|
import ResponseBlurb from '@/components/graph/widgets/chatHistory/ResponseBlurb.vue'
|
|
import type { ComponentWidget } from '@/scripts/domWidget'
|
|
import { linkifyHtml, nl2br } from '@/utils/formatUtil'
|
|
|
|
const { widget, history } = defineProps<{
|
|
widget?: ComponentWidget<string>
|
|
history: string
|
|
}>()
|
|
|
|
const editIndex = ref<number | null>(null)
|
|
const scrollPanelRef = ref<InstanceType<typeof ScrollPanel> | null>(null)
|
|
|
|
const parsedHistory = computed(() => JSON.parse(history || '[]'))
|
|
|
|
const findPromptInput = () =>
|
|
widget?.node.widgets?.find((w) => w.name === 'prompt')
|
|
let promptInput = findPromptInput()
|
|
const previousPromptInput = ref<string | null>(null)
|
|
|
|
const getPreviousResponseId = (index: number) =>
|
|
index > 0 ? parsedHistory.value[index - 1]?.response_id ?? '' : ''
|
|
|
|
const storePromptInput = () => {
|
|
promptInput ??= widget?.node.widgets?.find((w) => w.name === 'prompt')
|
|
if (!promptInput) return
|
|
|
|
previousPromptInput.value = String(promptInput.value)
|
|
}
|
|
|
|
const setPromptInput = (text: string, previousResponseId?: string | null) => {
|
|
promptInput ??= widget?.node.widgets?.find((w) => w.name === 'prompt')
|
|
if (!promptInput) return
|
|
|
|
if (previousResponseId !== null) {
|
|
promptInput.value = `<starting_point_id:${previousResponseId}>\n\n${text}`
|
|
} else {
|
|
promptInput.value = text
|
|
}
|
|
}
|
|
|
|
const handleEdit = (index: number) => {
|
|
promptInput ??= widget?.node.widgets?.find((w) => w.name === 'prompt')
|
|
editIndex.value = index
|
|
const prevResponseId = index === 0 ? 'start' : getPreviousResponseId(index)
|
|
const promptText = parsedHistory.value[index]?.prompt ?? ''
|
|
|
|
storePromptInput()
|
|
setPromptInput(promptText, prevResponseId)
|
|
}
|
|
|
|
const resetEditingState = () => {
|
|
editIndex.value = null
|
|
}
|
|
const handleCancelEdit = () => {
|
|
resetEditingState()
|
|
if (promptInput) {
|
|
promptInput.value = previousPromptInput.value ?? ''
|
|
}
|
|
}
|
|
|
|
const scrollChatToBottom = () => {
|
|
const content = document.getElementById('chat-scroll-content')
|
|
if (content) {
|
|
content.scrollTo({ top: content.scrollHeight, behavior: 'smooth' })
|
|
}
|
|
}
|
|
|
|
const onHistoryChanged = () => {
|
|
resetEditingState()
|
|
void nextTick(() => scrollChatToBottom())
|
|
}
|
|
|
|
watch(() => parsedHistory.value, onHistoryChanged, {
|
|
immediate: true,
|
|
deep: true
|
|
})
|
|
</script>
|