mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-03 13:48:49 +00:00
## Summary - **Pricing copy** — replace "concurrent API jobs" wording with "workflows via API" on Creator/Pro plans, add a third feature line to Standard, and add a new "Run Workflows via API" entry to the "What's Included" section (existing feature11 → feature12). - **Tutorial captions** — wire per-locale VTT caption tracks into `TutorialDetailDialog`, so the CC button in `VideoPlayer` is now functional for the learning tutorials. `LearningTutorial.caption` is typed as `readonly VideoTrack[]`; `VideoTrack` is now exported from `VideoPlayer.vue`. - Tailwind class ordering in `PriceSection.vue` shifted due to the formatter on touched lines. ## Test plan - [ ] `/pricing` page (EN + zh-CN): Standard plan shows the new "1 workflow via API" line; Creator/Pro show "3/5 workflows via API"; "What's Included" lists "Run Workflows via API" above "Parallel job execution". - [ ] Open a learning tutorial → CC button is visible in the player → toggling CC shows English captions in sync with playback. - [ ] Tutorials without a `caption` entry hide the CC button. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions <github-actions@github.com>
82 lines
2.5 KiB
Vue
82 lines
2.5 KiB
Vue
<script setup lang="ts">
|
|
import { onMounted, onUnmounted, useTemplateRef } from 'vue'
|
|
|
|
import type { LearningTutorial } from '../../data/learningTutorials'
|
|
import type { Locale } from '../../i18n/translations'
|
|
|
|
import { lockScroll, unlockScroll } from '../../composables/scrollLock'
|
|
import { t } from '../../i18n/translations'
|
|
import VideoPlayer from '../common/VideoPlayer.vue'
|
|
|
|
const { tutorial, locale = 'en' } = defineProps<{
|
|
tutorial: LearningTutorial
|
|
locale?: Locale
|
|
}>()
|
|
|
|
const emit = defineEmits<{ close: [] }>()
|
|
|
|
const dialogRef = useTemplateRef<HTMLDialogElement>('dialogRef')
|
|
|
|
function handleBackdropClick(e: MouseEvent) {
|
|
if (e.target === e.currentTarget) emit('close')
|
|
}
|
|
|
|
function handleKeydown(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') emit('close')
|
|
}
|
|
|
|
onMounted(() => {
|
|
lockScroll()
|
|
dialogRef.value?.showModal()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
unlockScroll()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Teleport to="body">
|
|
<dialog
|
|
ref="dialogRef"
|
|
:aria-label="tutorial.title[locale]"
|
|
class="fixed inset-0 z-50 flex size-full max-h-none max-w-none flex-col items-center justify-center border-0 bg-transparent px-4 py-8 backdrop-blur-xl backdrop:bg-transparent lg:px-20 lg:py-8"
|
|
@click="handleBackdropClick"
|
|
@keydown="handleKeydown"
|
|
@close="emit('close')"
|
|
>
|
|
<button
|
|
:aria-label="t('gallery.detail.close', locale)"
|
|
class="border-primary-comfy-yellow hover:bg-primary-comfy-yellow group absolute top-8 right-10 z-10 flex size-10 cursor-pointer items-center justify-center rounded-2xl border-2 bg-primary-comfy-ink transition-colors lg:right-26"
|
|
@click="emit('close')"
|
|
>
|
|
<span
|
|
class="bg-primary-comfy-yellow size-5 transition-colors group-hover:bg-primary-comfy-ink"
|
|
style="mask: url('/icons/close.svg') center / contain no-repeat"
|
|
/>
|
|
</button>
|
|
|
|
<div
|
|
class="border-primary-comfy-yellow rounded-5xl flex w-full max-w-7xl items-center justify-center overflow-hidden border-2 bg-primary-comfy-ink p-3 lg:p-4"
|
|
>
|
|
<VideoPlayer
|
|
:key="tutorial.id"
|
|
:locale
|
|
:src="tutorial.videoSrc"
|
|
:poster="tutorial.poster"
|
|
:tracks="tutorial.caption"
|
|
autoplay
|
|
class="w-full"
|
|
/>
|
|
</div>
|
|
|
|
<h2
|
|
class="mt-6 text-center text-lg font-medium text-primary-comfy-canvas lg:text-xl"
|
|
>
|
|
{{ t('learning.tutorials.titlePrefix', locale) }}
|
|
{{ tutorial.title[locale] }}
|
|
</h2>
|
|
</dialog>
|
|
</Teleport>
|
|
</template>
|