Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
94884d7
feat: add queue view toggle stub
benceruleanlu Dec 20, 2025
5ddea4e
Extract to component
benceruleanlu Dec 20, 2025
a0dad31
Add feature flag
benceruleanlu Dec 22, 2025
3d0c0d1
Move feature flag to setting
benceruleanlu Dec 31, 2025
dc53cbe
Add N active jobs and clear queue button
benceruleanlu Dec 22, 2025
2fd9a73
Readd divider as v-else
benceruleanlu Dec 23, 2025
0c6ea56
fix: read QPOV2 setting in assets sidebar
benceruleanlu Jan 1, 2026
5b14568
Add AssetsListCard base template
benceruleanlu Dec 22, 2025
da48899
Add AssetsListCard stories
benceruleanlu Dec 22, 2025
f614914
Remove view action from AssetsListCard story
benceruleanlu Dec 22, 2025
85c6825
Add list view
benceruleanlu Dec 23, 2025
627db67
knip
benceruleanlu Dec 23, 2025
afa4664
[automated] Apply ESLint and Prettier fixes
actions-user Dec 23, 2025
f128c61
Remove special failed job styling
benceruleanlu Dec 23, 2025
db3edd5
Add list view
benceruleanlu Dec 23, 2025
d8e57c6
Add stories for list view and general job card
benceruleanlu Dec 23, 2025
f78b6ee
Add and use knipIgnoreUnusedButUsedByStorybook
benceruleanlu Dec 23, 2025
79af715
fix: avoid unused export in job actions
benceruleanlu Jan 1, 2026
55db4fe
Merge remote-tracking branch 'origin/main' into bl-precious-earthworm
benceruleanlu Jan 6, 2026
5ed4e5f
Rename card to item
benceruleanlu Jan 7, 2026
9e477d4
Add select-none to list item
benceruleanlu Jan 7, 2026
e9d675e
refactor: share progress bar background helpers
benceruleanlu Jan 7, 2026
ede934a
Inline job card class
benceruleanlu Jan 7, 2026
114ece5
Inline list card base class
benceruleanlu Jan 7, 2026
f9eb1c4
Extract media icon mapping
benceruleanlu Jan 7, 2026
7c82218
refactor: simplify job cancel action
benceruleanlu Jan 7, 2026
9c4f808
knip
benceruleanlu Jan 7, 2026
72a9571
refactor: simplify job action sets
benceruleanlu Jan 7, 2026
ab09e7a
refactor: make job menu actions explicit
benceruleanlu Jan 7, 2026
a8fa3e3
feat: add icon slot to assets list item
benceruleanlu Jan 7, 2026
434ee52
fix: prevent active job items from shrinking
benceruleanlu Jan 7, 2026
578b8fb
Refactor job actions to single cancel and formalUtil extraction
benceruleanlu Jan 9, 2026
0f02b9f
refactor: make job cancel action reactive
benceruleanlu Jan 9, 2026
c63352c
Merge remote-tracking branch 'origin/bl-precious-earthworm' into bl-s…
benceruleanlu Jan 9, 2026
0fb02f9
Merge remote-tracking branch 'origin/main' into bl-spontaneous-guppy
benceruleanlu Jan 10, 2026
bc7a412
Update useJobActions mock alignment
benceruleanlu Jan 10, 2026
3126a04
Add storybook mocks to knip entry
benceruleanlu Jan 10, 2026
f6cbf02
Remove dead code branch
benceruleanlu Jan 10, 2026
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
29 changes: 26 additions & 3 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,32 @@ const config: StorybookConfig = {
allowedHosts: true
},
resolve: {
alias: {
'@': process.cwd() + '/src'
}
alias: [
{
find: '@/composables/queue/useJobList',
replacement: process.cwd() + '/src/storybook/mocks/useJobList.ts'
},
{
find: '@/composables/queue/useJobActions',
replacement: process.cwd() + '/src/storybook/mocks/useJobActions.ts'
},
{
find: '@/utils/formatUtil',
replacement:
process.cwd() +
'/packages/shared-frontend-utils/src/formatUtil.ts'
},
{
find: '@/utils/networkUtil',
replacement:
process.cwd() +
'/packages/shared-frontend-utils/src/networkUtil.ts'
},
{
find: '@',
replacement: process.cwd() + '/src'
}
]
},
esbuild: {
// Prevent minification of identifiers to preserve _sfc_main
Expand Down
3 changes: 2 additions & 1 deletion knip.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const config: KnipConfig = {
'src/assets/css/style.css',
'src/main.ts',
'src/scripts/ui/menu/index.ts',
'src/types/index.ts'
'src/types/index.ts',
'src/storybook/mocks/**/*.ts'
],
project: ['**/*.{js,ts,vue}', '*.{js,ts,mts}']
},
Expand Down
154 changes: 154 additions & 0 deletions src/components/sidebar/tabs/AssetsSidebarListView.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'

import type { JobAction } from '@/composables/queue/useJobActions'
import type { JobListItem } from '@/composables/queue/useJobList'
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
import { setMockJobActions } from '@/storybook/mocks/useJobActions'
import { setMockJobItems } from '@/storybook/mocks/useJobList'
import { iconForJobState } from '@/utils/queueDisplay'

import AssetsSidebarListView from './AssetsSidebarListView.vue'

type StoryArgs = {
assets: AssetItem[]
jobs: JobListItem[]
selectedAssetIds?: string[]
actionsByJobId?: Record<string, JobAction[]>
}

function baseDecorator() {
return {
template: `
<div class="bg-base-background p-6">
<story />
</div>
`
}
}

const meta: Meta<StoryArgs> = {
title: 'Components/Sidebar/AssetsSidebarListView',
component: AssetsSidebarListView,
parameters: {
layout: 'centered'
},
decorators: [baseDecorator]
}

export default meta
type Story = StoryObj<typeof meta>

const baseTimestamp = '2024-01-15T10:00:00Z'

const sampleJobs: JobListItem[] = [
{
id: 'job-pending-1',
title: 'In queue',
meta: '8:59:30pm',
state: 'pending',
iconName: iconForJobState('pending'),
showClear: true
},
{
id: 'job-init-1',
title: 'Initializing...',
meta: '8:59:35pm',
state: 'initialization',
iconName: iconForJobState('initialization'),
showClear: true
},
{
id: 'job-running-1',
title: 'Total: 30%',
meta: 'KSampler: 70%',
state: 'running',
iconName: iconForJobState('running'),
showClear: true,
progressTotalPercent: 30,
progressCurrentPercent: 70
}
]

const sampleAssets: AssetItem[] = [
{
id: 'asset-image-1',
name: 'image-032.png',
created_at: baseTimestamp,
preview_url: '/assets/images/comfy-logo-single.svg',
size: 1887437,
tags: [],
user_metadata: {
promptId: 'job-running-1',
nodeId: 12,
executionTimeInSeconds: 1.84
}
},
{
id: 'asset-video-1',
name: 'clip-01.mp4',
created_at: baseTimestamp,
preview_url: '/assets/images/default-template.png',
size: 8394820,
tags: [],
user_metadata: {
duration: 132000
}
},
{
id: 'asset-audio-1',
name: 'soundtrack-01.mp3',
created_at: baseTimestamp,
size: 5242880,
tags: [],
user_metadata: {
duration: 200000
}
},
{
id: 'asset-3d-1',
name: 'scene-01.glb',
created_at: baseTimestamp,
size: 134217728,
tags: []
}
]

const cancelAction: JobAction = {
icon: 'icon-[lucide--x]',
label: 'Cancel',
variant: 'destructive'
}

export const RunningAndGenerated: Story = {
args: {
assets: sampleAssets,
jobs: sampleJobs,
actionsByJobId: {
'job-pending-1': [cancelAction],
'job-init-1': [cancelAction],
'job-running-1': [cancelAction]
}
},
render: renderAssetsSidebarListView
}

function renderAssetsSidebarListView(args: StoryArgs) {
return {
components: { AssetsSidebarListView },
setup() {
setMockJobItems(args.jobs)
setMockJobActions(args.actionsByJobId ?? {})
const selectedIds = new Set(args.selectedAssetIds ?? [])
function isSelected(assetId: string) {
return selectedIds.has(assetId)
}

return { args, isSelected }
},
template: `
<div class="h-[520px] w-[320px] overflow-hidden rounded-lg border border-panel-border">
<AssetsSidebarListView :assets="args.assets" :is-selected="isSelected" />
</div>
`
}
}
2 changes: 1 addition & 1 deletion src/composables/queue/useJobActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { JobListItem } from '@/composables/queue/useJobList'
import { useJobMenu } from '@/composables/queue/useJobMenu'
import type { JobState } from '@/types/queue'

type JobAction = {
export type JobAction = {
icon: string
label: string
variant: 'destructive' | 'secondary' | 'textonly'
Expand Down
137 changes: 87 additions & 50 deletions src/platform/assets/components/AssetsListItem.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'

import Button from '@/components/ui/button/Button.vue'
import AssetsListItem from '@/platform/assets/components/AssetsListItem.vue'
import { iconForJobState } from '@/utils/queueDisplay'

const meta: Meta<typeof AssetsListItem> = {
title: 'Platform/Assets/AssetsListItem',
Expand All @@ -18,11 +19,95 @@ const meta: Meta<typeof AssetsListItem> = {

export default meta
type Story = StoryObj<typeof meta>

const IMAGE_PREVIEW = '/assets/images/comfy-logo-single.svg'
const VIDEO_PREVIEW = '/assets/images/default-template.png'

export const PendingJob: Story = {
args: {
iconName: iconForJobState('pending'),
iconClass: 'animate-spin',
primaryText: 'In queue',
secondaryText: '8:59:30pm'
}
}

export const InitializationJob: Story = {
args: {
iconName: iconForJobState('initialization'),
primaryText: 'Initializing...',
secondaryText: '8:59:35pm'
}
}

export const RunningJob: Story = {
args: {
iconName: iconForJobState('running'),
primaryText: 'Total: 30%',
secondaryText: 'CLIP Text Encode: 70%',
progressTotalPercent: 30,
progressCurrentPercent: 70
}
}

export const RunningJobWithActions: Story = {
args: {
iconName: iconForJobState('running'),
progressTotalPercent: 30,
progressCurrentPercent: 70
},
render: renderRunningJobWithActions
}

export const FailedJob: Story = {
args: {
iconName: iconForJobState('failed'),
iconClass: 'text-destructive-background',
iconWrapperClass: 'bg-modal-card-placeholder-background',
primaryText: 'Failed',
secondaryText: '8:59:30pm'
}
}

export const GeneratedImage: Story = {
args: {
previewUrl: IMAGE_PREVIEW,
previewAlt: 'image-032.png',
primaryText: 'image-032.png',
secondaryText: '1.84s'
}
}

export const GeneratedVideo: Story = {
args: {
previewUrl: VIDEO_PREVIEW,
previewAlt: 'clip-01.mp4',
primaryText: 'clip-01.mp4',
secondaryText: '2m 12s'
}
}

export const GeneratedAudio: Story = {
args: {
iconName: 'icon-[lucide--music]',
primaryText: 'soundtrack-01.mp3',
secondaryText: '3m 20s'
}
}

export const Generated3D: Story = {
args: {
iconName: 'icon-[lucide--box]',
primaryText: 'scene-01.glb',
secondaryText: '128 MB'
}
}

type AssetsListItemProps = InstanceType<typeof AssetsListItem>['$props']

function renderActiveJob(args: AssetsListItemProps) {
function renderRunningJobWithActions(args: AssetsListItemProps) {
return {
components: { Button, AssetsListItem },
components: { AssetsListItem, Button },
setup() {
return { args }
},
Expand All @@ -49,51 +134,3 @@ function renderActiveJob(args: AssetsListItemProps) {
`
}
}

function renderGeneratedAsset(args: AssetsListItemProps) {
return {
components: { AssetsListItem },
setup() {
return { args }
},
template: `
<AssetsListItem v-bind="args">
<template #secondary>
<div class="flex items-center gap-2 text-text-secondary">
<span>1m 56s</span>
<span>512x512</span>
</div>
</template>
</AssetsListItem>
`
}
}

export const ActiveJob: Story = {
args: {
previewUrl: '/assets/images/comfy-logo-single.svg',
previewAlt: 'Job preview',
progressTotalPercent: 30,
progressCurrentPercent: 70
},
render: renderActiveJob
}

export const FailedJob: Story = {
args: {
iconName: 'icon-[lucide--circle-alert]',
iconClass: 'text-destructive-background',
iconWrapperClass: 'bg-modal-card-placeholder-background',
primaryText: 'Failed',
secondaryText: '8:59:30pm'
}
}

export const GeneratedAsset: Story = {
args: {
previewUrl: '/assets/images/comfy-logo-single.svg',
previewAlt: 'image03.png',
primaryText: 'image03.png'
},
render: renderGeneratedAsset
}
Loading