Skip to content

Commit

Permalink
Enhancements to the queue image feed (#646)
Browse files Browse the repository at this point in the history
* Enhancements to the queue image feed
 - Change flat list icon
 - Add cover/contain mode
 - Add right click -> go to node
 - Add go to node link on detail

* Add loading spinner

* resolve comments

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
  • Loading branch information
pythongosssss and huchenlei authored Aug 27, 2024
1 parent 84662ad commit 9cdefca
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 69 deletions.
72 changes: 61 additions & 11 deletions src/components/common/ComfyImage.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
<!-- A image with placeholder fallback on error -->
<template>
<img
:src="src"
@error="handleImageError"
:class="[{ 'broken-image': imageBroken }, ...classArray]"
/>
<span
v-if="!imageBroken"
class="comfy-image-wrap"
:class="[{ contain: contain }]"
>
<img
v-if="contain"
:src="src"
@error="handleImageError"
:data-test="src"
class="comfy-image-blur"
:style="{ 'background-image': `url(${src})` }"
/>
<img
:src="src"
@error="handleImageError"
class="comfy-image-main"
:class="[...classArray]"
/>
</span>
<div v-if="imageBroken" class="broken-image-placeholder">
<i class="pi pi-image"></i>
<span>{{ $t('imageFailedToLoad') }}</span>
Expand All @@ -14,10 +29,16 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
const props = defineProps<{
src: string
class?: string | string[] | object
}>()
const props = withDefaults(
defineProps<{
src: string
class?: string | string[] | object
contain: boolean
}>(),
{
contain: false
}
)
const imageBroken = ref(false)
const handleImageError = (e: Event) => {
Expand All @@ -37,8 +58,37 @@ const classArray = computed(() => {
</script>

<style scoped>
.broken-image {
display: none;
.comfy-image-wrap {
display: contents;
}
.comfy-image-blur {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.comfy-image-main {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
z-index: 1;
}
.contain .comfy-image-wrap {
position: relative;
width: 100%;
height: 100%;
}
.contain .comfy-image-main {
object-fit: contain;
backdrop-filter: blur(10px);
position: absolute;
}
.broken-image-placeholder {
Expand Down
42 changes: 40 additions & 2 deletions src/components/sidebar/tabs/QueueSidebarTab.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
<template>
<SidebarTabTemplate :title="$t('sideToolbar.queue')">
<template #tool-buttons>
<Button
:icon="
imageFit === 'cover'
? 'pi pi-arrow-down-left-and-arrow-up-right-to-center'
: 'pi pi-arrow-up-right-and-arrow-down-left-from-center'
"
text
severity="secondary"
@click="toggleImageFit"
class="toggle-expanded-button"
v-tooltip="$t(`sideToolbar.queueTab.${imageFit}ImagePreview`)"
/>
<Button
v-if="isInFolderView"
icon="pi pi-arrow-left"
Expand All @@ -12,7 +24,7 @@
/>
<template v-else>
<Button
:icon="isExpanded ? 'pi pi-chevron-up' : 'pi pi-chevron-down'"
:icon="isExpanded ? 'pi pi-images' : 'pi pi-image'"
text
severity="secondary"
@click="toggleExpanded"
Expand Down Expand Up @@ -47,6 +59,11 @@
</div>
<div ref="loadMoreTrigger" style="height: 1px" />
</div>
<div v-else-if="queueStore.isLoading">
<ProgressSpinner
style="width: 50px; left: 50%; transform: translateX(-50%)"
/>
</div>
<div v-else>
<NoResultsPlaceholder
icon="pi pi-info-circle"
Expand Down Expand Up @@ -74,16 +91,22 @@ import Button from 'primevue/button'
import ConfirmPopup from 'primevue/confirmpopup'
import ContextMenu from 'primevue/contextmenu'
import type { MenuItem } from 'primevue/menuitem'
import ProgressSpinner from 'primevue/progressspinner'
import TaskItem from './queue/TaskItem.vue'
import ResultGallery from './queue/ResultGallery.vue'
import SidebarTabTemplate from './SidebarTabTemplate.vue'
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
import { TaskItemImpl, useQueueStore } from '@/stores/queueStore'
import { api } from '@/scripts/api'
import { ComfyNode } from '@/types/comfyWorkflow'
import { useSettingStore } from '@/stores/settingStore'
import { app } from '@/scripts/app'
const IMAGE_FIT = 'Comfy.Queue.ImageFit'
const confirm = useConfirm()
const toast = useToast()
const queueStore = useQueueStore()
const settingStore = useSettingStore()
const { t } = useI18n()
// Expanded view: show all outputs in a flat list.
Expand All @@ -95,6 +118,7 @@ const galleryActiveIndex = ref(-1)
// Folder view: only show outputs from a single selected task.
const folderTask = ref<TaskItemImpl | null>(null)
const isInFolderView = computed(() => folderTask.value !== null)
const imageFit = computed<string>(() => settingStore.get(IMAGE_FIT))
const ITEMS_PER_PAGE = 8
const SCROLL_THRESHOLD = 100 // pixels from bottom to trigger load
Expand Down Expand Up @@ -204,6 +228,7 @@ const onStatus = async () => {
const menu = ref(null)
const menuTargetTask = ref<TaskItemImpl | null>(null)
const menuTargetNode = ref<ComfyNode | null>(null)
const menuItems = computed<MenuItem[]>(() => [
{
label: t('delete'),
Expand All @@ -215,17 +240,26 @@ const menuItems = computed<MenuItem[]>(() => [
label: t('loadWorkflow'),
icon: 'pi pi-file-export',
command: () => menuTargetTask.value?.loadWorkflow()
},
{
label: t('goToNode'),
icon: 'pi pi-arrow-circle-right',
command: () => app.goToNode(menuTargetNode.value?.id),
visible: !!menuTargetNode.value
}
])
const handleContextMenu = ({
task,
event
event,
node
}: {
task: TaskItemImpl
event: Event
node?: ComfyNode
}) => {
menuTargetTask.value = task
menuTargetNode.value = node
menu.value?.show(event)
}
Expand All @@ -245,6 +279,10 @@ const exitFolderView = () => {
updateVisibleTasks()
}
const toggleImageFit = () => {
settingStore.set(IMAGE_FIT, imageFit.value === 'cover' ? 'contain' : 'cover')
}
onMounted(() => {
api.addEventListener('status', onStatus)
queueStore.update()
Expand Down
7 changes: 6 additions & 1 deletion src/components/sidebar/tabs/queue/ResultGallery.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
:showThumbnails="false"
>
<template #item="{ item }">
<ComfyImage :key="item.url" :src="item.url" class="galleria-image" />
<ComfyImage
:key="item.url"
:src="item.url"
:contain="false"
class="galleria-image"
/>
</template>
</Galleria>
</template>
Expand Down
21 changes: 12 additions & 9 deletions src/components/sidebar/tabs/queue/ResultItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
<template
v-if="result.mediaType === 'images' || result.mediaType === 'gifs'"
>
<ComfyImage
:src="result.url"
class="task-output-image"
:contain="imageFit === 'contain'"
/>
<div class="image-preview-mask">
<Button
icon="pi pi-eye"
Expand All @@ -11,7 +16,6 @@
rounded
/>
</div>
<ComfyImage :src="result.url" class="task-output-image" />
</template>
<!-- TODO: handle more media types -->
<div v-else class="task-result-preview">
Expand All @@ -25,7 +29,8 @@
import { ResultItemImpl } from '@/stores/queueStore'
import ComfyImage from '@/components/common/ComfyImage.vue'
import Button from 'primevue/button'
import { onMounted, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useSettingStore } from '@/stores/settingStore'
const props = defineProps<{
result: ResultItemImpl
Expand All @@ -36,6 +41,10 @@ const emit = defineEmits<{
}>()
const resultContainer = ref<HTMLElement | null>(null)
const settingStore = useSettingStore()
const imageFit = computed<string>(() =>
settingStore.get('Comfy.Queue.ImageFit')
)
onMounted(() => {
if (props.result.mediaType === 'images') {
Expand All @@ -58,13 +67,6 @@ onMounted(() => {
align-items: center;
}
:deep(.task-output-image) {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.image-preview-mask {
position: absolute;
left: 50%;
Expand All @@ -75,6 +77,7 @@ onMounted(() => {
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 1;
}
.result-container:hover .image-preview-mask {
Expand Down
21 changes: 18 additions & 3 deletions src/components/sidebar/tabs/queue/TaskItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
<div class="task-item-details">
<div class="tag-wrapper status-tag-group">
<Tag v-if="isFlatTask && task.isHistory" class="node-name-tag">
{{ node?.type }} (#{{ node?.id }})
<Button
class="task-node-link"
:label="`${node?.type} (#${node?.id})`"
link
size="small"
@click="app.goToNode(node?.id)"
/>
</Tag>
<Tag :severity="taskTagSeverity(task.displayStatus)">
<span v-html="taskStatusText(task.displayStatus)"></span>
Expand Down Expand Up @@ -59,6 +65,7 @@ import Tag from 'primevue/tag'
import ResultItem from './ResultItem.vue'
import { TaskItemDisplayStatus, type TaskItemImpl } from '@/stores/queueStore'
import { ComfyNode } from '@/types/comfyWorkflow'
import { app } from '@/scripts/app'
const props = defineProps<{
task: TaskItemImpl
Expand All @@ -77,13 +84,16 @@ const node: ComfyNode | null = flatOutputs.length
: null
const emit = defineEmits<{
(e: 'contextmenu', value: { task: TaskItemImpl; event: MouseEvent }): void
(
e: 'contextmenu',
value: { task: TaskItemImpl; event: MouseEvent; node?: ComfyNode }
): void
(e: 'preview', value: TaskItemImpl): void
(e: 'task-output-length-clicked', value: TaskItemImpl): void
}>()
const handleContextMenu = (e: MouseEvent) => {
emit('contextmenu', { task: props.task, event: e })
emit('contextmenu', { task: props.task, event: e, node })
}
const handlePreview = () => {
Expand Down Expand Up @@ -164,6 +174,11 @@ const formatTime = (time?: number) => {
justify-content: space-between;
align-items: center;
width: 100%;
z-index: 1;
}
.task-node-link {
padding: 2px;
}
/* In dark mode, transparent background color for tags is not ideal for tags that
Expand Down
5 changes: 4 additions & 1 deletion src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const messages = {
experimental: 'BETA',
deprecated: 'DEPR',
loadWorkflow: 'Load Workflow',
goToNode: 'Go to Node',
settings: 'Settings',
searchSettings: 'Search Settings',
searchNodes: 'Search Nodes',
Expand All @@ -45,7 +46,9 @@ const messages = {
},
queueTab: {
showFlatList: 'Show Flat List',
backToAllTasks: 'Back to All Tasks'
backToAllTasks: 'Back to All Tasks',
containImagePreview: 'Fill Image Preview',
coverImagePreview: 'Fit Image Preview'
}
}
},
Expand Down
Loading

0 comments on commit 9cdefca

Please sign in to comment.