Files
ComfyUI_frontend/src/platform/assets/components/VideoHelpDialog.vue
Luke Mino-Altherr 5db6d1af9a [feat] Add video help dialog to Upload Model flow (#7177)
## Summary
Adds an interactive video tutorial dialog to help users find CivitAI
model URLs during the Upload Model wizard.

## Changes
- **New Component**: Created reusable `VideoHelpDialog.vue` component
  - Full-width video player with floating close button
  - Configurable props: `videoUrl`, `loop`, `showControls`
  - Custom ESC key handling to prevent parent dialog from closing
  - Click backdrop to dismiss
  - 70% dark backdrop for better video focus
- **Upload Model Flow**: Integrated video help button in step 1 footer
  - "How do I find this?" button opens tutorial video
  - Video demonstrates finding model URLs on CivitAI
- PostHog tracking attribute maintained (`upload-model-step1-help-link`)

## Review Focus
- ESC key event handling uses capture phase to prevent propagation to
parent dialogs
- Component follows existing patterns from `MediaVideoTop.vue` and
`BaseModalLayout.vue`
- Video player accessibility (ARIA labels, keyboard navigation)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7177-feat-Add-video-help-dialog-to-Upload-Model-flow-2c06d73d36508148963ad9ee60038ea3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-05 18:38:52 -08:00

76 lines
1.7 KiB
Vue

<template>
<Dialog
v-model:visible="isVisible"
modal
:closable="false"
:close-on-escape="false"
:dismissable-mask="true"
:pt="{
root: { class: 'video-help-dialog' },
header: { class: '!hidden' },
content: { class: '!p-0' },
mask: { class: '!bg-black/70' }
}"
:style="{ width: '90vw', maxWidth: '800px' }"
>
<div class="relative">
<IconButton
class="absolute top-4 right-6 z-10"
:aria-label="$t('g.close')"
@click="isVisible = false"
>
<i class="pi pi-times text-sm" />
</IconButton>
<video
autoplay
muted
loop
:aria-label="ariaLabel"
class="w-full rounded-lg"
:src="videoUrl"
>
{{ $t('g.videoFailedToLoad') }}
</video>
</div>
</Dialog>
</template>
<script setup lang="ts">
import { useEventListener } from '@vueuse/core'
import Dialog from 'primevue/dialog'
import { onWatcherCleanup, watch } from 'vue'
import IconButton from '@/components/button/IconButton.vue'
const isVisible = defineModel<boolean>({ required: true })
const { videoUrl, ariaLabel = 'Help video' } = defineProps<{
videoUrl: string
ariaLabel?: string
}>()
const handleEscapeKey = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
event.stopImmediatePropagation()
event.stopPropagation()
event.preventDefault()
isVisible.value = false
}
}
// Add listener with capture phase to intercept before parent dialogs
// Only active when dialog is visible
watch(
isVisible,
(visible) => {
if (visible) {
const stop = useEventListener(document, 'keydown', handleEscapeKey, {
capture: true
})
onWatcherCleanup(stop)
}
},
{ immediate: true }
)
</script>