Skip to content

Add a markdown image preview for the node help menu. #4417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 106 additions & 1 deletion src/components/sidebar/tabs/nodeLibrary/NodeHelpPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<!-- Markdown fetched successfully -->
<div
v-else-if="!error"
ref="markdownContainer"
class="markdown-content"
v-html="renderedHelpHtml"
/>
Expand Down Expand Up @@ -81,17 +82,50 @@
</div>
</div>
</div>

<!-- Image Gallery for markdown images -->
<ResultGallery
v-model:activeIndex="galleryActiveIndex"
:all-gallery-items="galleryItems"
/>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia'
import Button from 'primevue/button'
import ProgressSpinner from 'primevue/progressspinner'
import { computed } from 'vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue'

import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { ResultItemImpl } from '@/stores/queueStore'
import { useNodeHelpStore } from '@/stores/workspace/nodeHelpStore'

import ResultGallery from '../queue/ResultGallery.vue'

// Custom class for external markdown images
class MarkdownImageItem extends ResultItemImpl {
private _externalUrl: string

constructor(externalUrl: string, filename: string, index: number) {
super({
filename,
subfolder: '',
type: 'output',
nodeId: `markdown-${index}`,
mediaType: 'images'
})
this._externalUrl = externalUrl
}

override get url(): string {
return this._externalUrl
}

override get urlWithTimestamp(): string {
return this._externalUrl
}
}

const { node } = defineProps<{ node: ComfyNodeDefImpl }>()

const nodeHelpStore = useNodeHelpStore()
Expand All @@ -101,6 +135,11 @@ defineEmits<{
(e: 'close'): void
}>()

// Gallery state for image preview
const markdownContainer = ref<HTMLElement | null>(null)
const galleryItems = ref<MarkdownImageItem[]>([])
const galleryActiveIndex = ref(-1)

const inputList = computed(() =>
Object.values(node.inputs).map((spec) => ({
name: spec.name,
Expand All @@ -116,6 +155,63 @@ const outputList = computed(() =>
tooltip: spec.tooltip || ''
}))
)

// Track if we've already setup image preview to avoid re-setup
const imagePreviewSetup = ref(false)

// Function to setup image preview functionality
function setupImagePreview() {
if (!markdownContainer.value || imagePreviewSetup.value) return

const imgs = Array.from(markdownContainer.value.querySelectorAll('img'))

// Only setup if there are images
if (imgs.length === 0) return

// Update gallery items - create proper MarkdownImageItem instances
galleryItems.value = imgs.map((img, index) => {
// Extract filename from URL
const url = new URL(img.src, window.location.origin)
const filename = url.pathname.split('/').pop() || `image-${index}.jpg`

return new MarkdownImageItem(img.src, filename, index)
})

// Add click handlers to images
imgs.forEach((img, index) => {
img.style.cursor = 'zoom-in'
img.onclick = (e) => {
e.preventDefault()
galleryActiveIndex.value = index
}
})

imagePreviewSetup.value = true
}

// Reset setup flag when content changes
function resetImagePreviewSetup() {
imagePreviewSetup.value = false
galleryItems.value = []
galleryActiveIndex.value = -1
}

// Watch for changes in rendered HTML and setup image preview
watch(renderedHelpHtml, async () => {
resetImagePreviewSetup()
await nextTick()
setupImagePreview()
})

// Watch for loading state changes
watch([isLoading, error], () => {
resetImagePreviewSetup()
})

onMounted(async () => {
await nextTick()
setupImagePreview()
})
</script>

<style scoped lang="postcss">
Expand Down Expand Up @@ -240,4 +336,13 @@ const outputList = computed(() =>
color: var(--p-text-color);
}
}

/* Add hover effect for clickable images */
.markdown-content :deep(img) {
transition: opacity 0.2s ease;
}

.markdown-content :deep(img:hover) {
opacity: 0.8;
}
</style>