mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-21 23:34:31 +00:00
## Summary
Backports PR #6381 (UI color updates & tweaks) to core/1.30 branch.
This includes:
- Text readability improvements
- Semantic color token consistency updates
- UI element styling refinements
- Component import path fixes (ComfyLogoTransparent → ComfyLogo)
## Merge Conflicts Resolved
- **ComfyMenuButton.vue**: Accepted cherry-picked version with import
changes
- **20 browser test snapshots**: Used backportee's copy as instructed
**Original commit**: fd236b3587
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6679-Backport-PR-6381-to-core-1-30-UI-color-updates-tweaks-2aa6d73d3650815689defd191e3dd86e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
135 lines
4.1 KiB
Vue
135 lines
4.1 KiB
Vue
<template>
|
|
<ScrollPanel
|
|
ref="scrollPanelRef"
|
|
class="min-h-[400px] w-full rounded-lg p-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="{
|
|
'pointer-events-none opacity-40': editIndex !== null && i > editIndex
|
|
}"
|
|
>
|
|
<div class="mb-1 flex justify-end">
|
|
<div
|
|
class="bg-smoke-300 dark-theme:bg-smoke-800 max-w-[80%] rounded-xl px-4 py-1 text-right"
|
|
>
|
|
<div class="break-words text-[12px]">{{ item.prompt }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-2 mr-1 flex justify-end">
|
|
<CopyButton :text="item.prompt" />
|
|
<Button
|
|
v-tooltip="
|
|
editIndex === i ? $t('chatHistory.cancelEditTooltip') : null
|
|
"
|
|
text
|
|
rounded
|
|
class="h-4! w-4! p-1! text-smoke-400 hover:text-smoke-600 hover:dark-theme:text-smoke-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="{
|
|
'pointer-events-none opacity-25': 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>
|