Keep fetching library pages after the first 50 so the whole folder is reachable.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+55
-33
@@ -634,24 +634,6 @@ const allCount = computed(() => {
|
||||
sort: 'date-desc'
|
||||
}).length
|
||||
})
|
||||
const folderChipCounts = computed(() => {
|
||||
const map: Record<string, { videos: number; images: number; music: number }> = {}
|
||||
for (const clip of clips.value) {
|
||||
if (clip.hasVideo === false) continue
|
||||
const row = map[clip.folderId] || (map[clip.folderId] = { videos: 0, images: 0, music: 0 })
|
||||
row.videos += 1
|
||||
}
|
||||
for (const still of stills.value) {
|
||||
if (still.role !== 'output') continue
|
||||
const row = map[still.folderId] || (map[still.folderId] = { videos: 0, images: 0, music: 0 })
|
||||
row.images += 1
|
||||
}
|
||||
for (const track of tracks.value) {
|
||||
const row = map[track.folderId] || (map[track.folderId] = { videos: 0, images: 0, music: 0 })
|
||||
row.music += 1
|
||||
}
|
||||
return map
|
||||
})
|
||||
const moveFolders = computed(() => folders.value.filter(folder => folder.id !== folderId.value))
|
||||
const focused = computed(() => entries.value.find(entry => entry.key === focusedKey.value) || null)
|
||||
const focusedStill = computed(() => {
|
||||
@@ -771,7 +753,8 @@ function toast(message: string) {
|
||||
}, 4200)
|
||||
}
|
||||
|
||||
function applyLibrary(data: unknown) {
|
||||
function applyLibrary(data: unknown, opts?: { replace?: boolean }) {
|
||||
const replace = opts?.replace !== false
|
||||
const payload = (data || {}) as {
|
||||
id?: string
|
||||
hideInput?: boolean
|
||||
@@ -780,6 +763,8 @@ function applyLibrary(data: unknown) {
|
||||
clips?: LibraryClip[]
|
||||
stills?: LibraryStill[]
|
||||
tracks?: LibraryTrack[]
|
||||
nextCursor?: string | null
|
||||
hasMore?: boolean
|
||||
}
|
||||
if (payload.id && !payload.folders && !payload.clips && !payload.stills && !payload.tracks) {
|
||||
if (typeof payload.hideInput === 'boolean') {
|
||||
@@ -792,29 +777,62 @@ function applyLibrary(data: unknown) {
|
||||
}
|
||||
return
|
||||
}
|
||||
folders.value = payload.folders || []
|
||||
clips.value = payload.clips || []
|
||||
stills.value = payload.stills || []
|
||||
tracks.value = payload.tracks || []
|
||||
if (payload.folders?.length) folders.value = payload.folders
|
||||
if (replace) {
|
||||
clips.value = payload.clips || []
|
||||
stills.value = payload.stills || []
|
||||
tracks.value = payload.tracks || []
|
||||
} else {
|
||||
const merge = <T extends { id: string }>(prev: T[], next: T[]) => {
|
||||
const map = new Map<string, T>()
|
||||
for (const item of prev) map.set(item.id, item)
|
||||
for (const item of next) map.set(item.id, item)
|
||||
return [...map.values()]
|
||||
}
|
||||
clips.value = merge(clips.value, payload.clips || [])
|
||||
stills.value = merge(stills.value, payload.stills || [])
|
||||
tracks.value = merge(tracks.value, payload.tracks || [])
|
||||
}
|
||||
libraryNextCursor.value = payload.nextCursor || null
|
||||
if (folderId.value !== 'all' && !folders.value.some(folder => folder.id === folderId.value)) {
|
||||
folderId.value = folders.value[0]?.id || 'all'
|
||||
}
|
||||
}
|
||||
|
||||
const libraryNextCursor = ref<string | null>(null)
|
||||
let libraryFetchGen = 0
|
||||
|
||||
async function fetchLibraryPage(cursor: string | null) {
|
||||
const qs = new URLSearchParams({ limit: '50' })
|
||||
if (folderId.value && folderId.value !== 'all') qs.set('folderId', folderId.value)
|
||||
if (filter.value === 'images') qs.set('type', 'images')
|
||||
else if (filter.value === 'videos') qs.set('type', 'videos')
|
||||
else if (filter.value === 'music') qs.set('type', 'music')
|
||||
if (cursor) qs.set('cursor', cursor)
|
||||
return await $fetch<any>(`/api/library?${qs.toString()}`)
|
||||
}
|
||||
|
||||
async function loadLibrary() {
|
||||
// First page of metadata only — never follow with a full light=1 catalog scan
|
||||
const first = await $fetch<any>('/api/library?limit=50')
|
||||
applyLibrary(first)
|
||||
// First page paints immediately; remaining pages append in the background
|
||||
const gen = ++libraryFetchGen
|
||||
const first = await fetchLibraryPage(null)
|
||||
if (gen !== libraryFetchGen) return
|
||||
applyLibrary(first, { replace: true })
|
||||
let cursor = first?.nextCursor || null
|
||||
while (cursor && gen === libraryFetchGen) {
|
||||
const page = await fetchLibraryPage(cursor)
|
||||
if (gen !== libraryFetchGen) return
|
||||
applyLibrary(page, { replace: false })
|
||||
cursor = page?.nextCursor || null
|
||||
}
|
||||
}
|
||||
|
||||
function folderChipCount(folder: LibraryFolder) {
|
||||
if (!folder.unlocked) return (folder.clipCount || 0) + (folder.imageCount || 0) + (folder.trackCount || 0)
|
||||
const row = folderChipCounts.value[folder.id]
|
||||
if (!row) return 0
|
||||
if (filter.value === 'videos') return row.videos
|
||||
if (filter.value === 'images') return row.images
|
||||
if (filter.value === 'music') return row.music
|
||||
return row.videos + row.images + row.music
|
||||
// Prefer server totals so chips match the full folder before pages finish appending
|
||||
if (filter.value === 'videos') return folder.clipCount || 0
|
||||
if (filter.value === 'images') return folder.imageCount || 0
|
||||
if (filter.value === 'music') return folder.trackCount || 0
|
||||
return (folder.clipCount || 0) + (folder.imageCount || 0) + (folder.trackCount || 0)
|
||||
}
|
||||
|
||||
function folderChipClass(id: string) {
|
||||
@@ -1280,6 +1298,10 @@ watch([folderId, filter], () => {
|
||||
watch(search, () => {
|
||||
if (scroller.value) scroller.value.scrollTop = 0
|
||||
})
|
||||
watch([folderId, filter], () => {
|
||||
if (scroller.value) scroller.value.scrollTop = 0
|
||||
void loadLibrary()
|
||||
})
|
||||
watch([cols, rowH], ([nextCols, nextRowH], [prevCols, prevRowH]) => {
|
||||
const el = scroller.value
|
||||
if (!el || !prevRowH) return
|
||||
|
||||
Reference in New Issue
Block a user