Skip to content

Commit f034bde

Browse files
committed
feat(ui): enhance favorites with example editing, media management, and workspace apply
- Add reproducibility example editing with media support in favorite editor - Add example apply to workspace sessions (pro-variable, image modes) - Harden favorites page routing, guards, and garden deduplication - Consolidate FavoriteCard into editor form with full test coverage
1 parent 2dc0b99 commit f034bde

38 files changed

Lines changed: 2763 additions & 856 deletions

packages/ui/src/components/FavoriteCard.vue

Lines changed: 0 additions & 488 deletions
This file was deleted.

packages/ui/src/components/FavoriteDetailPanel.vue

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
<NSpace :size="8" align="center" wrap>
2222
<NButton
23+
data-testid="favorite-detail-use"
2324
type="primary"
2425
@click="$emit('use', favorite)"
2526
>
@@ -29,6 +30,7 @@
2930
{{ t('favorites.manager.card.useNow') }}
3031
</NButton>
3132
<NButton
33+
data-testid="favorite-detail-copy"
3234
secondary
3335
@click="$emit('copy', favorite)"
3436
>
@@ -38,6 +40,7 @@
3840
{{ t('favorites.manager.card.copyContent') }}
3941
</NButton>
4042
<NButton
43+
data-testid="favorite-detail-fullscreen"
4144
quaternary
4245
@click="$emit('fullscreen', favorite)"
4346
>
@@ -47,6 +50,7 @@
4750
{{ t('common.fullscreen') }}
4851
</NButton>
4952
<NButton
53+
data-testid="favorite-detail-edit"
5054
quaternary
5155
@click="$emit('edit', favorite)"
5256
>
@@ -56,6 +60,7 @@
5660
{{ t('favorites.manager.card.edit') }}
5761
</NButton>
5862
<NButton
63+
data-testid="favorite-detail-delete"
5964
quaternary
6065
type="error"
6166
@click="$emit('delete', favorite)"
@@ -194,11 +199,17 @@
194199
name="reproducibility"
195200
:title="t('favorites.manager.preview.reproducibility.title')"
196201
>
197-
<FavoriteReproducibilityDisplay :reproducibility="reproducibility" />
202+
<FavoriteReproducibilityDisplay
203+
:reproducibility="reproducibility"
204+
:example-previews="reproducibilityExamplePreviews"
205+
@apply-example="handleApplyExample"
206+
/>
198207
</NCollapseItem>
199208
<NCollapseItem name="extra" :title="t('favorites.manager.preview.extraTitle')">
200209
<FavoritePreviewExtensionHost
201210
:favorite="favorite"
211+
:garden-snapshot-hidden-sections="promotedGardenSnapshotSections"
212+
garden-snapshot-source-only
202213
@favorite-updated="handleFavoriteUpdated"
203214
/>
204215
</NCollapseItem>
@@ -298,11 +309,17 @@
298309
name="reproducibility"
299310
:title="t('favorites.manager.preview.reproducibility.title')"
300311
>
301-
<FavoriteReproducibilityDisplay :reproducibility="reproducibility" />
312+
<FavoriteReproducibilityDisplay
313+
:reproducibility="reproducibility"
314+
:example-previews="reproducibilityExamplePreviews"
315+
@apply-example="handleApplyExample"
316+
/>
302317
</NCollapseItem>
303318
<NCollapseItem name="extra" :title="t('favorites.manager.preview.extraTitle')">
304319
<FavoritePreviewExtensionHost
305320
:favorite="favorite"
321+
:garden-snapshot-hidden-sections="promotedGardenSnapshotSections"
322+
garden-snapshot-source-only
306323
@favorite-updated="handleFavoriteUpdated"
307324
/>
308325
</NCollapseItem>
@@ -361,7 +378,7 @@ const props = withDefaults(defineProps<{
361378
362379
const emit = defineEmits<{
363380
'back': []
364-
'use': [favorite: FavoritePrompt]
381+
'use': [favorite: FavoritePrompt, options?: { applyExample?: boolean; exampleId?: string; exampleIndex?: number }]
365382
'copy': [favorite: FavoritePrompt]
366383
'edit': [favorite: FavoritePrompt]
367384
'delete': [favorite: FavoritePrompt]
@@ -374,8 +391,14 @@ const services = inject<Ref<AppServices | null> | null>('services', null)
374391
375392
const assetDataUrlCache = new Map<string, string>()
376393
const displayImages = ref<string[]>([])
394+
const promotedGardenSnapshotSections = ['metaInfo', 'cover', 'showcases', 'examples', 'variables']
395+
const reproducibilityExamplePreviews = ref<Array<{
396+
images: Array<{ assetId: string; source: string }>
397+
inputImages: Array<{ assetId: string; source: string }>
398+
}>>([])
377399
const activeImageIndex = ref(0)
378400
let resolveSequence = 0
401+
let reproducibilityResolveSequence = 0
379402
380403
const detailVariant = computed(() => (displayImages.value.length > 0 ? 'image' : 'text'))
381404
const activeImage = computed(() => displayImages.value[activeImageIndex.value] || '')
@@ -504,10 +527,51 @@ const refreshDisplayImages = async () => {
504527
activeImageIndex.value = 0
505528
}
506529
530+
const refreshReproducibilityExamplePreviews = async () => {
531+
const currentSequence = ++reproducibilityResolveSequence
532+
const favorite = props.favorite
533+
if (!favorite) {
534+
reproducibilityExamplePreviews.value = []
535+
return
536+
}
537+
538+
const parsed = parseFavoriteReproducibility(favorite)
539+
const resolveAssetPreviews = async (assetIds: string[]) => {
540+
const previewItems: Array<{ assetId: string; source: string }> = []
541+
for (const assetId of assetIds) {
542+
const source = (await resolveAssetIdsToDataUrls([assetId]))[0]
543+
if (currentSequence !== reproducibilityResolveSequence) return []
544+
if (source) {
545+
previewItems.push({ assetId, source })
546+
}
547+
}
548+
return previewItems
549+
}
550+
551+
const previews: Array<{
552+
images: Array<{ assetId: string; source: string }>
553+
inputImages: Array<{ assetId: string; source: string }>
554+
}> = []
555+
for (const example of parsed.examples) {
556+
const images = await resolveAssetPreviews(example.imageAssetIds)
557+
if (currentSequence !== reproducibilityResolveSequence) return
558+
const inputImages = await resolveAssetPreviews(example.inputImageAssetIds)
559+
if (currentSequence !== reproducibilityResolveSequence) return
560+
previews.push({
561+
images,
562+
inputImages,
563+
})
564+
}
565+
566+
if (currentSequence !== reproducibilityResolveSequence) return
567+
reproducibilityExamplePreviews.value = previews
568+
}
569+
507570
watch(
508571
() => props.favorite,
509572
() => {
510573
void refreshDisplayImages()
574+
void refreshReproducibilityExamplePreviews()
511575
},
512576
{ immediate: true },
513577
)
@@ -516,6 +580,7 @@ watch(
516580
() => [services?.value?.favoriteImageStorageService, services?.value?.imageStorageService],
517581
() => {
518582
void refreshDisplayImages()
583+
void refreshReproducibilityExamplePreviews()
519584
},
520585
)
521586
@@ -563,6 +628,11 @@ const formatDate = (timestamp: number) => {
563628
const handleFavoriteUpdated = (favoriteId: string) => {
564629
emit('favorite-updated', favoriteId)
565630
}
631+
632+
const handleApplyExample = (options: { exampleId?: string; exampleIndex: number }) => {
633+
if (!props.favorite) return
634+
emit('use', props.favorite, { ...options, applyExample: true })
635+
}
566636
</script>
567637

568638
<style scoped>

0 commit comments

Comments
 (0)