Files
ComfyUI_frontend/src/platform/workflow/sharing/composables/useAssetSections.ts
Alexander Brown 1bac5d9bdd feat: workflow sharing and ComfyHub publish flow (#8951)
## Summary

Add workflow sharing by URL and a multi-step ComfyHub publish wizard,
gated by feature flags and an optional profile gate.

## Changes

- **What**: Share dialog with URL generation and asset warnings;
ComfyHub publish wizard (Describe → Examples → Finish) with thumbnail
upload and tags; profile gate flow; shared workflow URL loader with
confirmation dialog
- **Dependencies**: None (new `sharing/` module under
`src/platform/workflow/`)

## Review Focus

- Three new feature flags: `workflow_sharing_enabled`,
`comfyhub_upload_enabled`, `comfyhub_profile_gate_enabled`
- Share service API contract and stale-share detection
(`workflowShareService.ts`)
- Publish wizard and profile gate state management
- Shared workflow URL loading and query-param preservation

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8951-feat-share-workflow-by-URL-30b6d73d3650813ebbfafdad775bfb33)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-03-05 16:33:06 -08:00

72 lines
1.6 KiB
TypeScript

import { partition } from 'es-toolkit'
import { computed, ref, watch } from 'vue'
import type { AssetInfo } from '@/schemas/apiSchema'
type SectionId = 'media' | 'models'
interface AssetSection {
id: SectionId
labelKey: string
items: AssetInfo[]
}
export function useAssetSections(items: () => AssetInfo[]) {
const sections = computed(() => {
const [models, media] = partition(items(), (a) => a.model)
const allSections: AssetSection[] = [
{
id: 'media',
labelKey: 'shareWorkflow.mediaLabel',
items: media
},
{
id: 'models',
labelKey: 'shareWorkflow.modelsLabel',
items: models
}
]
return allSections.filter((s) => s.items.length > 0)
})
const expandedSectionId = ref<SectionId | null>(null)
function getDefaultExpandedSection(
availableSections: AssetSection[]
): SectionId | null {
if (availableSections.length === 0) return null
return (
availableSections.find((s) => s.id === 'media')?.id ??
availableSections[0].id
)
}
watch(
sections,
(availableSections) => {
const hasExpanded = availableSections.some(
(s) => s.id === expandedSectionId.value
)
if (hasExpanded) return
expandedSectionId.value = getDefaultExpandedSection(availableSections)
},
{ immediate: true }
)
function onSectionOpenChange(sectionId: SectionId, open: boolean) {
if (open) {
expandedSectionId.value = sectionId
return
}
if (expandedSectionId.value === sectionId) {
expandedSectionId.value = null
}
}
return {
sections,
expandedSectionId,
onSectionOpenChange
}
}