Files
ComfyUI_frontend/src/platform/workflow/sharing/components/ShareAssetThumbnail.vue
Alexander Brown 416f96649b [backport cloud/1.40] feat: workflow sharing and ComfyHub publish flow (#9454)
Backport of #8951 to `cloud/1.40`.

Cherry-pick of merge commit `1bac5d9bddd2106b04f330733a45094df379b592`
with conflict resolution.

## Conflict Resolution

- **TopMenuSection.vue**: Kept both queue context menu (from target) and
share tooltip/button (from PR)
- **StatusBadge.vue**: Accepted PR version (adds `class` prop,
`badgeClass` computed with `cn()`)
- **WorkflowTab.vue**: Kept target branch version — PR's context menu
additions depend on `WorkflowActionsList.vue` and
`types/workflowMenuItem` which don't exist on `cloud/1.40`
- **composables/README.md**: Merged both — kept `useValueTransform` from
target, used `useWorkflowPersistenceV2` from PR
- **Textarea.vue**: Accepted PR version (new file, deleted in target)
- **Binary snapshot**: Accepted PR version
- **pnpm-lock.yaml**: Regenerated with `pnpm install`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9454-backport-cloud-1-40-feat-workflow-sharing-and-ComfyHub-publish-flow-31b6d73d3650813ebd55e0d2a24860ec)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-03-06 02:42:47 +00:00

68 lines
1.7 KiB
Vue

<template>
<div
class="relative flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted"
>
<Skeleton
v-if="normalizedPreviewUrl && isLoading"
class="absolute inset-0"
/>
<img
v-if="normalizedPreviewUrl && !error"
:src="normalizedPreviewUrl"
:alt="name"
:class="
cn(
'size-full object-cover transition-opacity duration-200',
isReady ? 'opacity-100' : 'opacity-0'
)
"
@error="
$emit('thumbnailError', { name, previewUrl: normalizedPreviewUrl })
"
/>
<i
v-if="!normalizedPreviewUrl || error"
class="icon-[lucide--image] size-4 text-muted-foreground"
/>
</div>
</template>
<script setup lang="ts">
import { useImage } from '@vueuse/core'
import { computed } from 'vue'
import Skeleton from '@/components/ui/skeleton/Skeleton.vue'
import { cn } from '@/utils/tailwindUtil'
const { name, previewUrl } = defineProps<{
name: string
previewUrl: string | null | undefined
}>()
defineEmits<{
thumbnailError: [{ name: string; previewUrl: string | null }]
}>()
const normalizedPreviewUrl = computed(() => {
if (typeof previewUrl !== 'string' || previewUrl.length === 0) return null
try {
const url = new URL(previewUrl, window.location.origin)
if (
!url.origin.includes('googleapis') &&
url.searchParams.has('filename') &&
!url.searchParams.has('res')
)
url.searchParams.set('res', '256')
return url.toString()
} catch {
return previewUrl
}
})
const imageOptions = computed(() => ({
src: normalizedPreviewUrl.value ?? ''
}))
const { isReady, isLoading, error } = useImage(imageOptions)
</script>