mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-25 01:04:06 +00:00
feat: Add sort functionality to Media Asset Panel (#6695)
## Overview Adds sort functionality to the Media Asset Panel. Users can sort assets by creation time in Cloud environments. ## Key Changes ### 1. Sort Functionality (Cloud Only) - "Newest first" (most recent) - "Oldest first" (oldest) - Sorting based on `create_time` field (output assets) - Sorting based on `created_at` field (input assets) - Sort button is only displayed in Cloud environments ### 2. create_time Field Integration **Related PR**: #6092 Implemented sort functionality using the `create_time` field introduced in PR #6092. Applied the code from that PR directly to the following files: - `src/schemas/apiSchema.ts`: Added `create_time` field to `zExtraData` - `src/stores/queueStore.ts`: Added `createTime` getter to `TaskItemImpl` - `src/platform/remote/comfyui/history/types/historyV2Types.ts`: Added `create_time` to History V2 API response types - `src/platform/remote/comfyui/history/adapters/v2ToV1Adapter.ts`: Pass through `create_time` in V2→V1 adapter - `src/platform/assets/composables/media/assetMappers.ts`: Include `create_time` in asset metadata ### 3. Component Structure Improvements Created new components following existing component styles for consistency: - **`MediaAssetSearchBar.vue`**: Component combining existing SearchBox with sort button - **`AssetSortButton.vue`**: Same structure as `MoreButton.vue` (IconButton + Popover) - **`MediaAssetSortMenu.vue`**: Same style as `MediaAssetMoreMenu.vue` (using IconTextButton) - **`AssetsSidebarTab.vue`**: Refactored to use `MediaAssetSearchBar` ### 4. Utility Usage - Improved sort logic using `es-toolkit`'s `sortBy` - Follows project guidelines (CLAUDE.md) ## Technical Details ### History V2 API's create_time - Cloud backend provides `create_time` (in milliseconds) through History V2 API - Enables accurate sorting by creation time - For input assets, uses existing `created_at` (ISO string) ### Sort Implementation Uses `es-toolkit`'s `sortBy` in `useMediaAssetFiltering` composable: ```typescript // Get timestamp from asset (either create_time or created_at) const getAssetTime = (asset: AssetItem): number => { return ( (asset.user_metadata?.create_time as number) ?? (asset.created_at ? new Date(asset.created_at).getTime() : 0) ) } // Sort by time if (sortBy.value === 'oldest') { return sortByUtil(searchFiltered.value, [getAssetTime]) } else { return sortByUtil(searchFiltered.value, [(asset) => -getAssetTime(asset)]) } ``` ## Testing - ✅ Typecheck passed - ✅ Lint passed - ✅ Format passed 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6695-feat-Add-sort-functionality-to-Media-Asset-Panel-2ab6d73d3650818c818ff3559875d869) by [Unito](https://www.unito.io) Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<AssetsSidebarTemplate>
|
||||
<template #top>
|
||||
<span v-if="!isInFolderView" class="font-bold">
|
||||
{{ $t('sideToolbar.mediaAssets') }}
|
||||
{{ $t('sideToolbar.mediaAssets.title') }}
|
||||
</span>
|
||||
<div v-else class="flex w-full items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -39,14 +39,11 @@
|
||||
<Tab value="input">{{ $t('sideToolbar.labels.imported') }}</Tab>
|
||||
<Tab value="output">{{ $t('sideToolbar.labels.generated') }}</Tab>
|
||||
</TabList>
|
||||
<!-- Search Bar -->
|
||||
<div class="pt-2">
|
||||
<SearchBox
|
||||
v-model="searchQuery"
|
||||
:placeholder="$t('sideToolbar.searchAssets')"
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
<!-- Filter Bar -->
|
||||
<MediaAssetFilterBar
|
||||
v-model:search-query="searchQuery"
|
||||
v-model:sort-by="sortBy"
|
||||
/>
|
||||
</template>
|
||||
<template #body>
|
||||
<!-- Loading state -->
|
||||
@@ -165,12 +162,12 @@ import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||
import TextButton from '@/components/button/TextButton.vue'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import VirtualGrid from '@/components/common/VirtualGrid.vue'
|
||||
import SearchBox from '@/components/input/SearchBox.vue'
|
||||
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
||||
import Tab from '@/components/tab/Tab.vue'
|
||||
import TabList from '@/components/tab/TabList.vue'
|
||||
import { t } from '@/i18n'
|
||||
import MediaAssetCard from '@/platform/assets/components/MediaAssetCard.vue'
|
||||
import MediaAssetFilterBar from '@/platform/assets/components/MediaAssetFilterBar.vue'
|
||||
import { useMediaAssets } from '@/platform/assets/composables/media/useMediaAssets'
|
||||
import { useAssetSelection } from '@/platform/assets/composables/useAssetSelection'
|
||||
import { useMediaAssetActions } from '@/platform/assets/composables/useMediaAssetActions'
|
||||
@@ -247,7 +244,8 @@ const baseAssets = computed(() => {
|
||||
})
|
||||
|
||||
// Use media asset filtering composable
|
||||
const { searchQuery, filteredAssets } = useMediaAssetFiltering(baseAssets)
|
||||
const { searchQuery, sortBy, filteredAssets } =
|
||||
useMediaAssetFiltering(baseAssets)
|
||||
|
||||
const displayAssets = computed(() => {
|
||||
return filteredAssets.value
|
||||
|
||||
Reference in New Issue
Block a user