mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
feat: Add generation time sort options to Media Asset Panel (#6698)
## Summary Add generation time-based sorting options to the Media Asset Panel ## Changes - **New sorting options**: - Generation time (longest first) - Sort by longest execution time - Generation time (fastest first) - Sort by shortest execution time - **Show only in Generated tab**: - Generation time sorting is only meaningful for output assets with `executionTimeInSeconds` metadata - Implemented conditional rendering via `showGenerationTimeSort` prop ## Technical Details - `useMediaAssetFiltering.ts`: - Added `'longest'` and `'fastest'` to `SortOption` type - Added `getAssetExecutionTime` helper function - Implemented sorting logic using switch-case pattern - `MediaAssetSortMenu.vue`: - Added `showGenerationTimeSort` prop - Generation time sort buttons placed inside `<template v-if="showGenerationTimeSort">` - `MediaAssetFilterBar.vue`: - Receives `showGenerationTimeSort` prop and passes it to `MediaAssetSortMenu` - `AssetsSidebarTab.vue`: - Passes `showGenerationTimeSort` prop based on `activeTab === 'output'` - `src/locales/en/main.json`: - Added `sortLongestFirst`: "Generation time (longest first)" - Added `sortFastestFirst`: "Generation time (fastest first)" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -36,13 +36,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Normal Tab View -->
|
<!-- Normal Tab View -->
|
||||||
<TabList v-else v-model="activeTab" class="pt-4 pb-1">
|
<TabList v-else v-model="activeTab" class="pt-4 pb-1">
|
||||||
<Tab value="input">{{ $t('sideToolbar.labels.imported') }}</Tab>
|
|
||||||
<Tab value="output">{{ $t('sideToolbar.labels.generated') }}</Tab>
|
<Tab value="output">{{ $t('sideToolbar.labels.generated') }}</Tab>
|
||||||
|
<Tab value="input">{{ $t('sideToolbar.labels.imported') }}</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
<!-- Filter Bar -->
|
<!-- Filter Bar -->
|
||||||
<MediaAssetFilterBar
|
<MediaAssetFilterBar
|
||||||
v-model:search-query="searchQuery"
|
v-model:search-query="searchQuery"
|
||||||
v-model:sort-by="sortBy"
|
v-model:sort-by="sortBy"
|
||||||
|
:show-generation-time-sort="activeTab === 'output'"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
|
|||||||
@@ -621,7 +621,9 @@
|
|||||||
"mediaAssets": {
|
"mediaAssets": {
|
||||||
"title": "Media Assets",
|
"title": "Media Assets",
|
||||||
"sortNewestFirst": "Newest first",
|
"sortNewestFirst": "Newest first",
|
||||||
"sortOldestFirst": "Oldest first"
|
"sortOldestFirst": "Oldest first",
|
||||||
|
"sortLongestFirst": "Generation time (longest first)",
|
||||||
|
"sortFastestFirst": "Generation time (fastest first)"
|
||||||
},
|
},
|
||||||
"backToAssets": "Back to all assets",
|
"backToAssets": "Back to all assets",
|
||||||
"searchAssets": "Search assets...",
|
"searchAssets": "Search assets...",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<template #default="{ close }">
|
<template #default="{ close }">
|
||||||
<MediaAssetSortMenu
|
<MediaAssetSortMenu
|
||||||
:sort-by="sortBy"
|
:sort-by="sortBy"
|
||||||
|
:show-generation-time-sort
|
||||||
:close="close"
|
:close="close"
|
||||||
@update:sort-by="handleSortChange"
|
@update:sort-by="handleSortChange"
|
||||||
/>
|
/>
|
||||||
@@ -29,23 +30,24 @@ import { isCloud } from '@/platform/distribution/types'
|
|||||||
import AssetSortButton from './MediaAssetSortButton.vue'
|
import AssetSortButton from './MediaAssetSortButton.vue'
|
||||||
import MediaAssetSortMenu from './MediaAssetSortMenu.vue'
|
import MediaAssetSortMenu from './MediaAssetSortMenu.vue'
|
||||||
|
|
||||||
interface MediaAssetSearchBarProps {
|
const { showGenerationTimeSort = false } = defineProps<{
|
||||||
searchQuery: string
|
searchQuery: string
|
||||||
sortBy: 'newest' | 'oldest'
|
sortBy: 'newest' | 'oldest' | 'longest' | 'fastest'
|
||||||
}
|
showGenerationTimeSort?: boolean
|
||||||
|
}>()
|
||||||
defineProps<MediaAssetSearchBarProps>()
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:searchQuery': [value: string]
|
'update:searchQuery': [value: string]
|
||||||
'update:sortBy': [value: 'newest' | 'oldest']
|
'update:sortBy': [value: 'newest' | 'oldest' | 'longest' | 'fastest']
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const handleSearchChange = (value: string | undefined) => {
|
const handleSearchChange = (value: string | undefined) => {
|
||||||
emit('update:searchQuery', value ?? '')
|
emit('update:searchQuery', value ?? '')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSortChange = (value: 'newest' | 'oldest') => {
|
const handleSortChange = (
|
||||||
|
value: 'newest' | 'oldest' | 'longest' | 'fastest'
|
||||||
|
) => {
|
||||||
emit('update:sortBy', value)
|
emit('update:sortBy', value)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -21,22 +21,53 @@
|
|||||||
<i v-if="sortBy === 'oldest'" class="icon-[lucide--check] size-4" />
|
<i v-if="sortBy === 'oldest'" class="icon-[lucide--check] size-4" />
|
||||||
</template>
|
</template>
|
||||||
</IconTextButton>
|
</IconTextButton>
|
||||||
|
|
||||||
|
<template v-if="showGenerationTimeSort">
|
||||||
|
<IconTextButton
|
||||||
|
type="transparent"
|
||||||
|
icon-position="right"
|
||||||
|
:label="$t('sideToolbar.mediaAssets.sortLongestFirst')"
|
||||||
|
@click="handleSortChange('longest')"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i v-if="sortBy === 'longest'" class="icon-[lucide--check] size-4" />
|
||||||
|
</template>
|
||||||
|
</IconTextButton>
|
||||||
|
|
||||||
|
<IconTextButton
|
||||||
|
type="transparent"
|
||||||
|
icon-position="right"
|
||||||
|
:label="$t('sideToolbar.mediaAssets.sortFastestFirst')"
|
||||||
|
@click="handleSortChange('fastest')"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i v-if="sortBy === 'fastest'" class="icon-[lucide--check] size-4" />
|
||||||
|
</template>
|
||||||
|
</IconTextButton>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
|
|
||||||
const { sortBy, close } = defineProps<{
|
const {
|
||||||
sortBy: 'newest' | 'oldest'
|
sortBy,
|
||||||
|
close,
|
||||||
|
showGenerationTimeSort = false
|
||||||
|
} = defineProps<{
|
||||||
|
sortBy: 'newest' | 'oldest' | 'longest' | 'fastest'
|
||||||
close: () => void
|
close: () => void
|
||||||
|
showGenerationTimeSort?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:sortBy': [value: 'newest' | 'oldest']
|
'update:sortBy': [value: 'newest' | 'oldest' | 'longest' | 'fastest']
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const handleSortChange = (value: 'newest' | 'oldest') => {
|
const handleSortChange = (
|
||||||
|
value: 'newest' | 'oldest' | 'longest' | 'fastest'
|
||||||
|
) => {
|
||||||
emit('update:sortBy', value)
|
emit('update:sortBy', value)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { Ref } from 'vue'
|
|||||||
|
|
||||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||||
|
|
||||||
type SortOption = 'newest' | 'oldest'
|
type SortOption = 'newest' | 'oldest' | 'longest' | 'fastest'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get timestamp from asset (either create_time or created_at)
|
* Get timestamp from asset (either create_time or created_at)
|
||||||
@@ -18,6 +18,13 @@ const getAssetTime = (asset: AssetItem): number => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get execution time from asset user_metadata
|
||||||
|
*/
|
||||||
|
const getAssetExecutionTime = (asset: AssetItem): number => {
|
||||||
|
return (asset.user_metadata?.executionTimeInSeconds as number) ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Media Asset Filtering composable
|
* Media Asset Filtering composable
|
||||||
* Manages search, filter, and sort for media assets
|
* Manages search, filter, and sort for media assets
|
||||||
@@ -46,12 +53,24 @@ export function useMediaAssetFiltering(assets: Ref<AssetItem[]>) {
|
|||||||
|
|
||||||
const filteredAssets = computed(() => {
|
const filteredAssets = computed(() => {
|
||||||
// Sort by create_time (output assets) or created_at (input assets)
|
// Sort by create_time (output assets) or created_at (input assets)
|
||||||
if (sortBy.value === 'oldest') {
|
switch (sortBy.value) {
|
||||||
// Ascending order (oldest first)
|
case 'oldest':
|
||||||
return sortByUtil(searchFiltered.value, [getAssetTime])
|
// Ascending order (oldest first)
|
||||||
} else {
|
return sortByUtil(searchFiltered.value, [getAssetTime])
|
||||||
// Descending order (newest first) - negate for descending
|
case 'longest':
|
||||||
return sortByUtil(searchFiltered.value, [(asset) => -getAssetTime(asset)])
|
// Descending order (longest execution time first)
|
||||||
|
return sortByUtil(searchFiltered.value, [
|
||||||
|
(asset) => -getAssetExecutionTime(asset)
|
||||||
|
])
|
||||||
|
case 'fastest':
|
||||||
|
// Ascending order (fastest execution time first)
|
||||||
|
return sortByUtil(searchFiltered.value, [getAssetExecutionTime])
|
||||||
|
case 'newest':
|
||||||
|
default:
|
||||||
|
// Descending order (newest first) - negate for descending
|
||||||
|
return sortByUtil(searchFiltered.value, [
|
||||||
|
(asset) => -getAssetTime(asset)
|
||||||
|
])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user