diff --git a/packages/imajin-react/src/PipelinesPage/index.tsx b/packages/imajin-react/src/PipelinesPage/index.tsx index 9f0d660c..2a3600f9 100644 --- a/packages/imajin-react/src/PipelinesPage/index.tsx +++ b/packages/imajin-react/src/PipelinesPage/index.tsx @@ -2,10 +2,10 @@ * Image Pipelines Page * * Admin interface for managing image generation pipelines. - * Features pipeline cards with stats and ImageGen Assistant. + * Features pipeline cards with stats, ImageGen Assistant, and Identity Generation. */ -import { useState } from 'react'; +import { useState, useCallback } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Grid, Stack } from '@lilith/ui-layout'; import { Modal, Tabs } from '@lilith/ui-feedback'; @@ -14,6 +14,8 @@ import type { PipelineConfig, ParsedPrompt } from '@lilith/imajin-app'; import { PIPELINES, getServiceUrls } from '@lilith/imajin-app'; import { ImageGenAssistant } from '../ImageGenAssistant'; +import { IdentityGenerator } from '../IdentityGenerator'; +import type { IdentityGenerationResult } from '../hooks/useIdentityGeneration'; import { Card, CardTitle, @@ -49,6 +51,10 @@ export interface GalleryItem { export interface ImagePipelinesPageProps { /** Base URL for imagegen-assistant service */ assistantBaseUrl?: string; + /** Base URL for imajin-identity service (default: http://localhost:8009) */ + identityServiceUrl?: string; + /** Base URL for imajin-pipeline service (default: http://localhost:8080) */ + pipelineServiceUrl?: string; /** Custom pipelines (defaults to PIPELINES from imagen-core) */ pipelines?: PipelineConfig[]; /** Function to fetch queue stats for a category */ @@ -60,6 +66,8 @@ export interface ImagePipelinesPageProps { pipeline: PipelineConfig, prompts: ParsedPrompt[], ) => Promise; + /** Called when an identity-generated image should be added to gallery */ + onIdentityImageGenerated?: (result: IdentityGenerationResult) => void; } // ============================================================================ @@ -124,17 +132,44 @@ function PipelineCardComponent({ export function ImagePipelinesPage({ assistantBaseUrl = getServiceUrls().imagegenAssistant, + identityServiceUrl, + pipelineServiceUrl, pipelines = PIPELINES, fetchQueueStats, fetchGalleryImages, submitPrompts, + onIdentityImageGenerated, }: ImagePipelinesPageProps) { const [selectedPipeline, setSelectedPipeline] = useState(null); - const [activeTab, setActiveTab] = useState<'pipelines' | 'gallery'>('pipelines'); + const [activeTab, setActiveTab] = useState<'pipelines' | 'gallery' | 'identity'>('pipelines'); const [galleryCategory, setGalleryCategory] = useState( pipelines[0]?.category ?? 'skeleton', ); + // Track identity-generated images for gallery + const [identityGalleryImages, setIdentityGalleryImages] = useState([]); + + const handleIdentityImageGenerated = useCallback((result: IdentityGenerationResult) => { + // Add to local gallery + const newImage: GalleryItem = { + src: result.imageBase64.startsWith('data:') + ? result.imageBase64 + : `data:image/png;base64,${result.imageBase64}`, + alt: `Identity generated - ${result.model}`, + title: `Seed: ${result.seed} | Match: ${(result.identityMatchScore * 100).toFixed(0)}%`, + }; + setIdentityGalleryImages((prev) => [newImage, ...prev]); + + // Also call external handler if provided + onIdentityImageGenerated?.(result); + }, [onIdentityImageGenerated]); + + const handleSaveToGallery = useCallback((result: IdentityGenerationResult) => { + handleIdentityImageGenerated(result); + // Optionally switch to gallery tab to show the saved image + setActiveTab('gallery'); + }, [handleIdentityImageGenerated]); + // Fetch gallery images const { data: galleryImages } = useQuery({ queryKey: ['gallery-images', galleryCategory], @@ -165,10 +200,11 @@ export function ImagePipelinesPage({ setActiveTab(key as 'pipelines' | 'gallery')} + onTabChange={(key) => setActiveTab(key as 'pipelines' | 'gallery' | 'identity')} /> {activeTab === 'pipelines' && ( @@ -188,20 +224,38 @@ export function ImagePipelinesPage({ )} + {activeTab === 'identity' && ( + + )} + {activeTab === 'gallery' && (
- Generated Images ({galleryItems.length}) - {galleryItems.length > 0 ? ( + + Generated Images ({galleryItems.length + identityGalleryImages.length}) + + {galleryItems.length + identityGalleryImages.length > 0 ? ( + {/* Identity-generated images first (most recent) */} + {identityGalleryImages.map((item, i) => ( + + {item.alt} + + ))} + {/* Pipeline-generated images */} {galleryItems.map((item, i) => ( - + {item.alt} ))} ) : ( - No images generated yet. Use the Assistant to create prompts and generate images. + No images generated yet. Use the Pipelines or Identity Generation tabs to create images. )}