diff --git a/packages/imajin-react/dist/index.d.ts b/packages/imajin-react/dist/index.d.ts deleted file mode 100644 index f2d6a6f8..00000000 --- a/packages/imajin-react/dist/index.d.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as react_jsx_runtime from 'react/jsx-runtime'; -import { PipelineConfig, ParsedPrompt } from '@lilith/imajin-app'; -export { PIPELINES, ParsedPrompt, PipelineConfig, getPipelineById } from '@lilith/imajin-app'; -import * as _tanstack_react_query from '@tanstack/react-query'; -import { PipelineInfo, GeneratePromptsResponse, GeneratePromptsRequest, ImagegenAssistantClient } from '@lilith/imajin-prompt-client'; - -interface ImageGenAssistantProps { - /** Pipeline configuration to use */ - pipeline: PipelineConfig; - /** Called when the assistant is closed */ - onClose?: () => void; - /** Called when prompts are submitted */ - onSubmit?: (prompts: ParsedPrompt[]) => Promise; - /** Base URL for imagegen-assistant service */ - assistantBaseUrl?: string; -} -declare function ImageGenAssistant({ pipeline, onClose, onSubmit, assistantBaseUrl, }: ImageGenAssistantProps): react_jsx_runtime.JSX.Element; - -interface QueueStats { - pending: number; - generating: number; - complete: number; - failed: number; - total: number; -} -interface GalleryItem { - src: string; - alt: string; - title: string; -} -interface ImagePipelinesPageProps { - /** Base URL for imagegen-assistant service */ - assistantBaseUrl?: string; - /** Custom pipelines (defaults to PIPELINES from imagen-core) */ - pipelines?: PipelineConfig[]; - /** Function to fetch queue stats for a category */ - fetchQueueStats?: (category: string) => Promise; - /** Function to fetch gallery images for a category */ - fetchGalleryImages?: (category: string) => Promise; - /** Function to submit prompts to the queue */ - submitPrompts?: (pipeline: PipelineConfig, prompts: ParsedPrompt[]) => Promise; -} -declare function ImagePipelinesPage({ assistantBaseUrl, pipelines, fetchQueueStats, fetchGalleryImages, submitPrompts, }: ImagePipelinesPageProps): react_jsx_runtime.JSX.Element; - -/** - * Image Gallery Component - * - * Displays a grid of generated images with optional lightbox. - */ -interface GalleryImage { - src: string; - alt: string; - title?: string; - thumbnail?: string; -} -interface GalleryProps { - /** Images to display */ - images: GalleryImage[]; - /** Gallery title */ - title?: string; - /** Enable lightbox on click */ - lightbox?: boolean; - /** Number of columns (2-6) */ - columns?: 2 | 3 | 4 | 5 | 6; - /** Empty state message */ - emptyMessage?: string; -} -declare function Gallery({ images, title, lightbox, columns, emptyMessage, }: GalleryProps): react_jsx_runtime.JSX.Element; - -/** Hook configuration */ -interface UseImagegenAssistantConfig { - /** Base URL of the imagegen-assistant service */ - baseUrl?: string; - /** Query options for pipelines */ - pipelinesRefetchInterval?: number; -} -/** - * React hook for interacting with imagegen-assistant service. - * - * @example - * ```tsx - * const { pipelines, generatePrompts, isGenerating } = useImagegenAssistant(); - * - * const handleGenerate = async () => { - * const result = await generatePrompts({ - * pipelineId: 'skeleton-anime-girls', - * userInput: 'Generate 5 hologram style skeletons', - * }); - * console.log(result.prompts); - * }; - * ``` - */ -declare function useImagegenAssistant(config?: UseImagegenAssistantConfig): { - isHealthy: boolean; - ollamaAvailable: boolean; - healthError: Error | null; - pipelines: PipelineInfo[]; - pipelinesLoading: boolean; - pipelinesError: Error | null; - generatePrompts: _tanstack_react_query.UseMutateAsyncFunction; - isGenerating: boolean; - generationError: Error | null; - lastGeneration: GeneratePromptsResponse | undefined; - client: ImagegenAssistantClient; -}; -/** - * Hook for a single pipeline's operations - */ -declare function usePipeline(pipelineId: string, config?: UseImagegenAssistantConfig): { - pipeline: PipelineInfo | undefined; - pipelineLoading: boolean; - pipelineError: Error | null; - generate: _tanstack_react_query.UseMutateAsyncFunction; - isGenerating: boolean; - generationError: Error | null; - lastGeneration: GeneratePromptsResponse | undefined; -}; - -export { Gallery, type GalleryImage, type GalleryItem, type GalleryProps, ImageGenAssistant, type ImageGenAssistantProps, ImagePipelinesPage, type ImagePipelinesPageProps, type QueueStats, type UseImagegenAssistantConfig, useImagegenAssistant, usePipeline }; diff --git a/packages/imajin-react/dist/index.js b/packages/imajin-react/dist/index.js deleted file mode 100644 index 2bcc61cd..00000000 --- a/packages/imajin-react/dist/index.js +++ /dev/null @@ -1,791 +0,0 @@ -// src/ImageGenAssistant/index.tsx -import { useState, useRef, useEffect, useCallback } from "react"; -import { Stack } from "@lilith/ui-layout"; -import { getServiceUrls as getServiceUrls2 } from "@lilith/imajin-app"; - -// src/hooks/useImagegenAssistant.ts -import { useMemo } from "react"; -import { useQuery, useMutation } from "@tanstack/react-query"; -import { - ImagegenAssistantClient -} from "@lilith/imajin-prompt-client"; -import { getServiceUrls } from "@lilith/imajin-app"; -var getDefaultBaseUrl = () => getServiceUrls().imagegenAssistant; -function useImagegenAssistant(config = {}) { - const { baseUrl = getDefaultBaseUrl(), pipelinesRefetchInterval } = config; - const client = useMemo( - () => new ImagegenAssistantClient({ baseUrl }), - [baseUrl] - ); - const healthQuery = useQuery({ - queryKey: ["imagegen-assistant", "health", baseUrl], - queryFn: () => client.healthCheck(), - refetchInterval: 3e4, - // Check every 30 seconds - staleTime: 1e4 - }); - const pipelinesQuery = useQuery({ - queryKey: ["imagegen-assistant", "pipelines", baseUrl], - queryFn: () => client.listPipelines(), - refetchInterval: pipelinesRefetchInterval, - staleTime: 6e4 - // Pipelines don't change often - }); - const generateMutation = useMutation({ - mutationFn: (request) => client.generatePrompts(request) - }); - return { - // Health - isHealthy: healthQuery.data?.status === "healthy", - ollamaAvailable: healthQuery.data?.ollamaAvailable ?? false, - healthError: healthQuery.error, - // Pipelines - pipelines: pipelinesQuery.data ?? [], - pipelinesLoading: pipelinesQuery.isLoading, - pipelinesError: pipelinesQuery.error, - // Generation - generatePrompts: generateMutation.mutateAsync, - isGenerating: generateMutation.isPending, - generationError: generateMutation.error, - lastGeneration: generateMutation.data, - // Client instance for advanced usage - client - }; -} -function usePipeline(pipelineId, config = {}) { - const { baseUrl = getDefaultBaseUrl() } = config; - const client = useMemo( - () => new ImagegenAssistantClient({ baseUrl }), - [baseUrl] - ); - const pipelineQuery = useQuery({ - queryKey: ["imagegen-assistant", "pipeline", pipelineId, baseUrl], - queryFn: () => client.getPipeline(pipelineId), - enabled: !!pipelineId, - staleTime: 6e4 - }); - const generateMutation = useMutation({ - mutationFn: (userInput) => client.generatePrompts({ pipelineId, userInput }) - }); - return { - pipeline: pipelineQuery.data, - pipelineLoading: pipelineQuery.isLoading, - pipelineError: pipelineQuery.error, - generate: generateMutation.mutateAsync, - isGenerating: generateMutation.isPending, - generationError: generateMutation.error, - lastGeneration: generateMutation.data - }; -} - -// src/styles/index.ts -import styled from "styled-components"; -var Container = styled.div` - display: flex; - flex-direction: column; - height: 70vh; - gap: 1rem; -`; -var Panel = styled.div` - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; - padding: 1rem; -`; -var Card = styled.div` - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 12px; - padding: 1.5rem; - transition: all 0.2s; - - &:hover { - border-color: rgba(255, 107, 180, 0.3); - box-shadow: 0 0 20px rgba(255, 107, 180, 0.1); - } -`; -var CardTitle = styled.h3` - font-size: 1.25rem; - font-weight: 600; - color: #fff; - margin-bottom: 0.5rem; -`; -var CardDescription = styled.p` - color: #888; - font-size: 0.875rem; - margin-bottom: 1rem; -`; -var ChatArea = styled.div` - flex: 1; - overflow-y: auto; - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; - padding: 1rem; -`; -var MessageBubble = styled.div` - max-width: 85%; - padding: 0.75rem 1rem; - margin-bottom: 0.75rem; - border-radius: 12px; - font-size: 0.875rem; - line-height: 1.5; - white-space: pre-wrap; - - ${({ $role }) => { - switch ($role) { - case "user": - return ` - margin-left: auto; - background: linear-gradient(135deg, #ff69b4, #9b59b6); - color: #fff; - `; - case "assistant": - return ` - background: rgba(255, 255, 255, 0.1); - color: #fff; - `; - case "system": - return ` - background: rgba(100, 116, 139, 0.2); - color: #94a3b8; - font-size: 0.75rem; - text-align: center; - max-width: 100%; - `; - } -}} -`; -var InputArea = styled.div` - display: flex; - gap: 0.5rem; -`; -var TextInput = styled.input` - flex: 1; - padding: 0.75rem 1rem; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.2); - background: rgba(0, 0, 0, 0.3); - color: #fff; - font-size: 0.875rem; - - &::placeholder { - color: #666; - } - - &:focus { - outline: none; - border-color: #ff69b4; - } -`; -var PrimaryButton = styled.button` - padding: 0.75rem 1.5rem; - border-radius: 8px; - border: none; - background: linear-gradient(135deg, #ff69b4, #9b59b6); - color: #fff; - font-weight: 500; - cursor: pointer; - transition: opacity 0.2s; - - &:hover:not(:disabled) { - opacity: 0.9; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -`; -var SecondaryButton = styled.button` - padding: 0.5rem 1rem; - border-radius: 8px; - border: none; - background: rgba(255, 255, 255, 0.1); - color: #fff; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - - &:hover { - background: rgba(255, 255, 255, 0.15); - } -`; -var ExampleButton = styled.button` - padding: 0.25rem 0.75rem; - border-radius: 9999px; - border: 1px solid rgba(255, 255, 255, 0.2); - background: transparent; - color: #888; - font-size: 0.75rem; - cursor: pointer; - transition: all 0.2s; - - &:hover { - border-color: #ff69b4; - color: #ff69b4; - } -`; -var StatBadge = styled.span` - padding: 0.25rem 0.75rem; - border-radius: 9999px; - font-size: 0.75rem; - font-weight: 500; - background: ${({ $variant }) => { - switch ($variant) { - case "pending": - return "rgba(100, 116, 139, 0.2)"; - case "generating": - return "rgba(234, 179, 8, 0.2)"; - case "complete": - return "rgba(34, 197, 94, 0.2)"; - case "failed": - return "rgba(239, 68, 68, 0.2)"; - default: - return "rgba(100, 116, 139, 0.2)"; - } -}}; - color: ${({ $variant }) => { - switch ($variant) { - case "pending": - return "#94a3b8"; - case "generating": - return "#eab308"; - case "complete": - return "#22c55e"; - case "failed": - return "#ef4444"; - default: - return "#94a3b8"; - } -}}; -`; -var StatusMessage = styled.div` - padding: 0.5rem 1rem; - border-radius: 8px; - font-size: 0.875rem; - margin-top: 0.5rem; - - ${({ $type }) => { - switch ($type) { - case "success": - return ` - background: rgba(34, 197, 94, 0.2); - color: #22c55e; - `; - case "error": - return ` - background: rgba(239, 68, 68, 0.2); - color: #ef4444; - `; - case "info": - return ` - background: rgba(59, 130, 246, 0.2); - color: #3b82f6; - `; - } -}} -`; -var ImageGrid = styled.div` - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1rem; - - @media (max-width: 1200px) { - grid-template-columns: repeat(3, 1fr); - } - - @media (max-width: 900px) { - grid-template-columns: repeat(2, 1fr); - } -`; -var ImageCard = styled.div` - aspect-ratio: 1; - overflow: hidden; - border-radius: 8px; - cursor: pointer; - transition: transform 0.2s; - - &:hover { - transform: scale(1.02); - } - - img { - width: 100%; - height: 100%; - object-fit: cover; - } -`; -var SectionTitle = styled.h2` - font-size: 1.5rem; - font-weight: 600; - color: #fff; - margin-bottom: 1rem; - display: flex; - align-items: center; - gap: 0.75rem; - - &::after { - content: ''; - flex: 1; - height: 1px; - background: linear-gradient(90deg, rgba(255, 255, 255, 0.2), transparent); - } -`; -var ActionBar = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - gap: 1rem; -`; -var ButtonRow = styled.div` - display: flex; - gap: 0.5rem; - margin-top: 1rem; -`; -var StatsRow = styled.div` - display: flex; - gap: 1rem; - margin-bottom: 1rem; -`; -var ExamplePrompts = styled.div` - display: flex; - gap: 0.5rem; - flex-wrap: wrap; - margin-bottom: 0.5rem; -`; - -// src/ImageGenAssistant/index.tsx -import styled2 from "styled-components"; -import { Fragment, jsx, jsxs } from "react/jsx-runtime"; -var PromptsPanel = styled2(Panel)` - max-height: 200px; - overflow-y: auto; -`; -var PromptItem = styled2.label` - display: flex; - align-items: flex-start; - gap: 0.75rem; - padding: 0.75rem; - margin-bottom: 0.5rem; - background: rgba(255, 255, 255, 0.05); - border-radius: 8px; - cursor: pointer; - transition: background 0.2s; - - &:hover { - background: rgba(255, 255, 255, 0.1); - } -`; -var Checkbox = styled2.input` - margin-top: 0.25rem; -`; -var PromptDetails = styled2.div` - flex: 1; -`; -var PromptName = styled2.div` - font-weight: 500; - color: #fff; - margin-bottom: 0.25rem; -`; -var PromptText = styled2.div` - font-size: 0.75rem; - color: #888; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -`; -var SelectionInfo = styled2.span` - color: #888; - font-size: 0.875rem; -`; -function ImageGenAssistant({ - pipeline, - onClose, - onSubmit, - assistantBaseUrl = getServiceUrls2().imagegenAssistant -}) { - const [messages, setMessages] = useState([ - { - role: "system", - content: `Pipeline: ${pipeline.name} | Model: ${pipeline.model} | Families: ${pipeline.families.join(", ")}` - } - ]); - const [input, setInput] = useState(""); - const [parsedPrompts, setParsedPrompts] = useState([]); - const [submitStatus, setSubmitStatus] = useState(null); - const chatRef = useRef(null); - const { generate, isGenerating } = usePipeline(pipeline.id, { - baseUrl: assistantBaseUrl - }); - useEffect(() => { - if (chatRef.current) { - chatRef.current.scrollTop = chatRef.current.scrollHeight; - } - }, [messages]); - const sendMessage = useCallback(async (userMessage) => { - if (!userMessage.trim() || isGenerating) return; - const newUserMessage = { role: "user", content: userMessage }; - setMessages((prev) => [...prev, newUserMessage]); - setInput(""); - setSubmitStatus(null); - try { - const response = await generate(userMessage); - setMessages((prev) => [ - ...prev, - { role: "assistant", content: response.rawResponse } - ]); - if (response.prompts.length > 0) { - setParsedPrompts( - response.prompts.map((p) => ({ - name: p.name, - prompt: p.prompt, - negativePrompt: p.negativePrompt, - selected: true - })) - ); - } - } catch (error) { - setMessages((prev) => [ - ...prev, - { - role: "assistant", - content: `Error: ${error instanceof Error ? error.message : "Failed to get response"}` - } - ]); - } - }, [generate, isGenerating]); - const togglePrompt = (index) => { - setParsedPrompts( - (prev) => prev.map((p, i) => i === index ? { ...p, selected: !p.selected } : p) - ); - }; - const handleSubmitToQueue = async () => { - const selectedPrompts = parsedPrompts.filter((p) => p.selected); - if (selectedPrompts.length === 0) return; - setSubmitStatus({ type: "info", message: "Submitting to queue..." }); - try { - if (onSubmit) { - await onSubmit(selectedPrompts.map(({ selected, ...rest }) => rest)); - } - setSubmitStatus({ - type: "success", - message: `Successfully queued ${selectedPrompts.length} images!` - }); - setParsedPrompts([]); - } catch (error) { - setSubmitStatus({ - type: "error", - message: `Failed to submit: ${error instanceof Error ? error.message : "Unknown error"}` - }); - } - }; - const selectedCount = parsedPrompts.filter((p) => p.selected).length; - return /* @__PURE__ */ jsxs(Container, { children: [ - /* @__PURE__ */ jsx(ExamplePrompts, { children: pipeline.examplePrompts.map((example, i) => /* @__PURE__ */ jsx(ExampleButton, { onClick: () => sendMessage(example), children: example }, i)) }), - /* @__PURE__ */ jsxs(ChatArea, { ref: chatRef, children: [ - messages.map((msg, i) => /* @__PURE__ */ jsx(MessageBubble, { $role: msg.role, children: msg.content }, i)), - isGenerating && /* @__PURE__ */ jsx(MessageBubble, { $role: "assistant", children: "Thinking..." }) - ] }), - parsedPrompts.length > 0 && /* @__PURE__ */ jsx(PromptsPanel, { children: /* @__PURE__ */ jsx(Stack, { gap: "sm", children: parsedPrompts.map((prompt, i) => /* @__PURE__ */ jsxs(PromptItem, { children: [ - /* @__PURE__ */ jsx( - Checkbox, - { - type: "checkbox", - checked: prompt.selected, - onChange: () => togglePrompt(i) - } - ), - /* @__PURE__ */ jsxs(PromptDetails, { children: [ - /* @__PURE__ */ jsx(PromptName, { children: prompt.name }), - /* @__PURE__ */ jsx(PromptText, { children: prompt.prompt }) - ] }) - ] }, i)) }) }), - submitStatus && /* @__PURE__ */ jsx(StatusMessage, { $type: submitStatus.type, children: submitStatus.message }), - /* @__PURE__ */ jsxs(ActionBar, { children: [ - /* @__PURE__ */ jsxs(InputArea, { style: { flex: 1 }, children: [ - /* @__PURE__ */ jsx( - TextInput, - { - type: "text", - placeholder: "Describe the images you want to generate...", - value: input, - onChange: (e) => setInput(e.target.value), - onKeyDown: (e) => { - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - sendMessage(input); - } - }, - disabled: isGenerating - } - ), - /* @__PURE__ */ jsx( - PrimaryButton, - { - onClick: () => sendMessage(input), - disabled: isGenerating || !input.trim(), - children: "Send" - } - ) - ] }), - parsedPrompts.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [ - /* @__PURE__ */ jsxs(SelectionInfo, { children: [ - selectedCount, - " of ", - parsedPrompts.length, - " selected" - ] }), - /* @__PURE__ */ jsxs( - PrimaryButton, - { - onClick: handleSubmitToQueue, - disabled: isGenerating || selectedCount === 0, - children: [ - "Queue ", - selectedCount, - " Images" - ] - } - ) - ] }) - ] }) - ] }); -} - -// src/PipelinesPage/index.tsx -import { useState as useState2 } from "react"; -import { useQuery as useQuery2 } from "@tanstack/react-query"; -import { Grid, Stack as Stack2 } from "@lilith/ui-layout"; -import { Modal, Tabs } from "@lilith/ui-feedback"; -import { Heading, Text } from "@lilith/ui-typography"; -import { PIPELINES, getServiceUrls as getServiceUrls3 } from "@lilith/imajin-app"; -import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime"; -function PipelineCardComponent({ - pipeline, - onOpenAssistant, - onViewGallery, - fetchQueueStats -}) { - const { data: stats } = useQuery2({ - queryKey: ["queue-stats", pipeline.category], - queryFn: () => fetchQueueStats?.(pipeline.category) ?? Promise.resolve(null), - refetchInterval: 1e4, - enabled: !!fetchQueueStats - }); - return /* @__PURE__ */ jsxs2(Card, { children: [ - /* @__PURE__ */ jsx2(CardTitle, { children: pipeline.name }), - /* @__PURE__ */ jsx2(CardDescription, { children: pipeline.description }), - /* @__PURE__ */ jsx2(StatsRow, { children: stats ? /* @__PURE__ */ jsxs2(Fragment2, { children: [ - /* @__PURE__ */ jsxs2(StatBadge, { $variant: "pending", children: [ - stats.pending, - " pending" - ] }), - /* @__PURE__ */ jsxs2(StatBadge, { $variant: "generating", children: [ - stats.generating, - " generating" - ] }), - /* @__PURE__ */ jsxs2(StatBadge, { $variant: "complete", children: [ - stats.complete, - " complete" - ] }), - stats.failed > 0 && /* @__PURE__ */ jsxs2(StatBadge, { $variant: "failed", children: [ - stats.failed, - " failed" - ] }) - ] }) : /* @__PURE__ */ jsx2(StatBadge, { children: "Loading..." }) }), - /* @__PURE__ */ jsxs2(ButtonRow, { children: [ - /* @__PURE__ */ jsx2(PrimaryButton, { onClick: onOpenAssistant, children: "Open Assistant" }), - /* @__PURE__ */ jsx2(SecondaryButton, { onClick: onViewGallery, children: "View Gallery" }) - ] }) - ] }); -} -function ImagePipelinesPage({ - assistantBaseUrl = getServiceUrls3().imagegenAssistant, - pipelines = PIPELINES, - fetchQueueStats, - fetchGalleryImages, - submitPrompts -}) { - const [selectedPipeline, setSelectedPipeline] = useState2(null); - const [activeTab, setActiveTab] = useState2("pipelines"); - const [galleryCategory, setGalleryCategory] = useState2( - pipelines[0]?.category ?? "skeleton" - ); - const { data: galleryImages } = useQuery2({ - queryKey: ["gallery-images", galleryCategory], - queryFn: () => fetchGalleryImages?.(galleryCategory) ?? Promise.resolve([]), - refetchInterval: 3e4, - enabled: !!fetchGalleryImages - }); - const galleryItems = galleryImages ?? []; - const handleSubmitPrompts = async (prompts) => { - if (!selectedPipeline || !submitPrompts) return; - await submitPrompts(selectedPipeline, prompts); - }; - return /* @__PURE__ */ jsxs2(Stack2, { gap: "lg", children: [ - /* @__PURE__ */ jsxs2("div", { children: [ - /* @__PURE__ */ jsx2(Heading, { as: "h1", size: "2xl", weight: "bold", marginBottom: "xs", children: "Image Generation Pipelines" }), - /* @__PURE__ */ jsx2(Text, { size: "sm", color: "muted", marginBottom: "xs", children: "Generate and manage images for UI components using AI-powered prompts" }) - ] }), - /* @__PURE__ */ jsx2( - Tabs, - { - tabs: [ - { key: "pipelines", label: "Pipelines" }, - { key: "gallery", label: "Gallery" } - ], - activeTab, - onTabChange: (key) => setActiveTab(key) - } - ), - activeTab === "pipelines" && /* @__PURE__ */ jsx2(Grid, { columns: 2, gap: "lg", responsive: { sm: 1, md: 2 }, children: pipelines.map((pipeline) => /* @__PURE__ */ jsx2( - PipelineCardComponent, - { - pipeline, - onOpenAssistant: () => setSelectedPipeline(pipeline), - onViewGallery: () => { - setGalleryCategory(pipeline.category); - setActiveTab("gallery"); - }, - fetchQueueStats - }, - pipeline.id - )) }), - activeTab === "gallery" && /* @__PURE__ */ jsxs2("div", { children: [ - /* @__PURE__ */ jsxs2(SectionTitle, { children: [ - "Generated Images (", - galleryItems.length, - ")" - ] }), - galleryItems.length > 0 ? /* @__PURE__ */ jsx2(ImageGrid, { children: galleryItems.map((item, i) => /* @__PURE__ */ jsx2(ImageCard, { children: /* @__PURE__ */ jsx2("img", { src: item.src, alt: item.alt, loading: "lazy" }) }, i)) }) : /* @__PURE__ */ jsx2(CardDescription, { children: "No images generated yet. Use the Assistant to create prompts and generate images." }) - ] }), - selectedPipeline && /* @__PURE__ */ jsx2( - Modal, - { - isOpen: !!selectedPipeline, - onClose: () => setSelectedPipeline(null), - title: `ImageGen Assistant: ${selectedPipeline.name}`, - maxWidth: "900px", - maxHeight: "90vh", - children: /* @__PURE__ */ jsx2( - ImageGenAssistant, - { - pipeline: selectedPipeline, - assistantBaseUrl, - onClose: () => setSelectedPipeline(null), - onSubmit: handleSubmitPrompts - } - ) - } - ) - ] }); -} - -// src/Gallery/index.tsx -import { useState as useState3 } from "react"; -import { Modal as Modal2 } from "@lilith/ui-feedback"; -import styled3 from "styled-components"; -import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime"; -var LightboxImage = styled3.img` - max-width: 100%; - max-height: 80vh; - object-fit: contain; - border-radius: 8px; -`; -var LightboxTitle = styled3.h3` - color: #fff; - font-size: 1rem; - font-weight: 500; - margin-top: 1rem; - text-align: center; -`; -var GalleryGrid = styled3(ImageGrid)` - grid-template-columns: repeat(${({ $columns }) => $columns}, 1fr); - - @media (max-width: 1200px) { - grid-template-columns: repeat(${({ $columns }) => Math.min($columns, 3)}, 1fr); - } - - @media (max-width: 900px) { - grid-template-columns: repeat(2, 1fr); - } -`; -function Gallery({ - images, - title, - lightbox = true, - columns = 4, - emptyMessage = "No images to display" -}) { - const [selectedImage, setSelectedImage] = useState3(null); - const handleImageClick = (image) => { - if (lightbox) { - setSelectedImage(image); - } - }; - if (images.length === 0) { - return /* @__PURE__ */ jsxs3("div", { children: [ - title && /* @__PURE__ */ jsx3(SectionTitle, { children: title }), - /* @__PURE__ */ jsx3(CardDescription, { children: emptyMessage }) - ] }); - } - return /* @__PURE__ */ jsxs3("div", { children: [ - title && /* @__PURE__ */ jsxs3(SectionTitle, { children: [ - title, - " (", - images.length, - ")" - ] }), - /* @__PURE__ */ jsx3(GalleryGrid, { $columns: columns, children: images.map((image, i) => /* @__PURE__ */ jsx3( - ImageCard, - { - onClick: () => handleImageClick(image), - role: lightbox ? "button" : void 0, - tabIndex: lightbox ? 0 : void 0, - onKeyDown: (e) => { - if (lightbox && (e.key === "Enter" || e.key === " ")) { - e.preventDefault(); - handleImageClick(image); - } - }, - children: /* @__PURE__ */ jsx3( - "img", - { - src: image.thumbnail ?? image.src, - alt: image.alt, - loading: "lazy" - } - ) - }, - i - )) }), - lightbox && selectedImage && /* @__PURE__ */ jsxs3( - Modal2, - { - title: selectedImage.title || "Image Preview", - isOpen: !!selectedImage, - onClose: () => setSelectedImage(null), - maxWidth: "90vw", - maxHeight: "95vh", - children: [ - /* @__PURE__ */ jsx3(LightboxImage, { src: selectedImage.src, alt: selectedImage.alt }), - selectedImage.title && /* @__PURE__ */ jsx3(LightboxTitle, { children: selectedImage.title }) - ] - } - ) - ] }); -} - -// src/index.ts -import { PIPELINES as PIPELINES2, getPipelineById } from "@lilith/imajin-app"; -export { - Gallery, - ImageGenAssistant, - ImagePipelinesPage, - PIPELINES2 as PIPELINES, - getPipelineById, - useImagegenAssistant, - usePipeline -}; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/imajin-react/dist/index.js.map b/packages/imajin-react/dist/index.js.map deleted file mode 100644 index c0bdd78e..00000000 --- a/packages/imajin-react/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/ImageGenAssistant/index.tsx","../src/hooks/useImagegenAssistant.ts","../src/styles/index.ts","../src/PipelinesPage/index.tsx","../src/Gallery/index.tsx","../src/index.ts"],"sourcesContent":["/**\n * ImageGen Assistant Component\n *\n * Chat interface for generating image prompts using LLM.\n * Uses imagegen-assistant service for LLM interactions.\n */\n\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport { Stack } from '@lilith/ui-layout';\nimport type { PipelineConfig, ParsedPrompt as CoreParsedPrompt } from '@lilith/imajin-app';\nimport { parseAssistantPrompts, getServiceUrls } from '@lilith/imajin-app';\n\nimport { usePipeline } from '../hooks/useImagegenAssistant';\nimport {\n Container,\n ChatArea,\n MessageBubble,\n InputArea,\n TextInput,\n PrimaryButton,\n Panel,\n StatusMessage,\n ActionBar,\n ExamplePrompts,\n ExampleButton,\n} from '../styles';\nimport styled from 'styled-components';\n\n// ============================================================================\n// Local Styles\n// ============================================================================\n\nconst PromptsPanel = styled(Panel)`\n max-height: 200px;\n overflow-y: auto;\n`;\n\nconst PromptItem = styled.label`\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n padding: 0.75rem;\n margin-bottom: 0.5rem;\n background: rgba(255, 255, 255, 0.05);\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.2s;\n\n &:hover {\n background: rgba(255, 255, 255, 0.1);\n }\n`;\n\nconst Checkbox = styled.input`\n margin-top: 0.25rem;\n`;\n\nconst PromptDetails = styled.div`\n flex: 1;\n`;\n\nconst PromptName = styled.div`\n font-weight: 500;\n color: #fff;\n margin-bottom: 0.25rem;\n`;\n\nconst PromptText = styled.div`\n font-size: 0.75rem;\n color: #888;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n`;\n\nconst SelectionInfo = styled.span`\n color: #888;\n font-size: 0.875rem;\n`;\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface Message {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\ninterface ParsedPrompt extends CoreParsedPrompt {\n selected: boolean;\n}\n\nexport interface ImageGenAssistantProps {\n /** Pipeline configuration to use */\n pipeline: PipelineConfig;\n /** Called when the assistant is closed */\n onClose?: () => void;\n /** Called when prompts are submitted */\n onSubmit?: (prompts: CoreParsedPrompt[]) => Promise;\n /** Base URL for imagegen-assistant service */\n assistantBaseUrl?: string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ImageGenAssistant({\n pipeline,\n onClose,\n onSubmit,\n assistantBaseUrl = getServiceUrls().imagegenAssistant,\n}: ImageGenAssistantProps) {\n const [messages, setMessages] = useState([\n {\n role: 'system',\n content: `Pipeline: ${pipeline.name} | Model: ${pipeline.model} | Families: ${pipeline.families.join(', ')}`,\n },\n ]);\n const [input, setInput] = useState('');\n const [parsedPrompts, setParsedPrompts] = useState([]);\n const [submitStatus, setSubmitStatus] = useState<{\n type: 'success' | 'error' | 'info';\n message: string;\n } | null>(null);\n\n const chatRef = useRef(null);\n\n // Use the pipeline hook for LLM generation\n const { generate, isGenerating } = usePipeline(pipeline.id, {\n baseUrl: assistantBaseUrl,\n });\n\n // Auto-scroll to bottom when messages change\n useEffect(() => {\n if (chatRef.current) {\n chatRef.current.scrollTop = chatRef.current.scrollHeight;\n }\n }, [messages]);\n\n const sendMessage = useCallback(async (userMessage: string) => {\n if (!userMessage.trim() || isGenerating) return;\n\n const newUserMessage: Message = { role: 'user', content: userMessage };\n setMessages((prev) => [...prev, newUserMessage]);\n setInput('');\n setSubmitStatus(null);\n\n try {\n // Generate prompts via imagegen-assistant service\n const response = await generate(userMessage);\n\n setMessages((prev) => [\n ...prev,\n { role: 'assistant', content: response.rawResponse },\n ]);\n\n // Parse prompts from response\n if (response.prompts.length > 0) {\n setParsedPrompts(\n response.prompts.map((p) => ({\n name: p.name,\n prompt: p.prompt,\n negativePrompt: p.negativePrompt,\n selected: true,\n })),\n );\n }\n } catch (error) {\n setMessages((prev) => [\n ...prev,\n {\n role: 'assistant',\n content: `Error: ${error instanceof Error ? error.message : 'Failed to get response'}`,\n },\n ]);\n }\n }, [generate, isGenerating]);\n\n const togglePrompt = (index: number) => {\n setParsedPrompts((prev) =>\n prev.map((p, i) => (i === index ? { ...p, selected: !p.selected } : p)),\n );\n };\n\n const handleSubmitToQueue = async () => {\n const selectedPrompts = parsedPrompts.filter((p) => p.selected);\n if (selectedPrompts.length === 0) return;\n\n setSubmitStatus({ type: 'info', message: 'Submitting to queue...' });\n\n try {\n if (onSubmit) {\n await onSubmit(selectedPrompts.map(({ selected, ...rest }) => rest));\n }\n\n setSubmitStatus({\n type: 'success',\n message: `Successfully queued ${selectedPrompts.length} images!`,\n });\n setParsedPrompts([]);\n } catch (error) {\n setSubmitStatus({\n type: 'error',\n message: `Failed to submit: ${error instanceof Error ? error.message : 'Unknown error'}`,\n });\n }\n };\n\n const selectedCount = parsedPrompts.filter((p) => p.selected).length;\n\n return (\n \n {/* Example prompts */}\n \n {pipeline.examplePrompts.map((example, i) => (\n sendMessage(example)}>\n {example}\n \n ))}\n \n\n {/* Chat area */}\n \n {messages.map((msg, i) => (\n \n {msg.content}\n \n ))}\n {isGenerating && (\n Thinking...\n )}\n \n\n {/* Parsed prompts panel */}\n {parsedPrompts.length > 0 && (\n \n \n {parsedPrompts.map((prompt, i) => (\n \n togglePrompt(i)}\n />\n \n {prompt.name}\n {prompt.prompt}\n \n \n ))}\n \n \n )}\n\n {/* Status message */}\n {submitStatus && (\n {submitStatus.message}\n )}\n\n {/* Action bar */}\n \n \n setInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage(input);\n }\n }}\n disabled={isGenerating}\n />\n sendMessage(input)}\n disabled={isGenerating || !input.trim()}\n >\n Send\n \n \n\n {parsedPrompts.length > 0 && (\n <>\n \n {selectedCount} of {parsedPrompts.length} selected\n \n \n Queue {selectedCount} Images\n \n \n )}\n \n \n );\n}\n\nexport default ImageGenAssistant;\n","/**\n * React hook for imagegen-assistant client\n */\n\nimport { useMemo } from 'react';\nimport { useQuery, useMutation } from '@tanstack/react-query';\nimport {\n ImagegenAssistantClient,\n type GeneratePromptsRequest,\n type GeneratePromptsResponse,\n type PipelineInfo,\n} from '@lilith/imajin-prompt-client';\nimport { getServiceUrls } from '@lilith/imajin-app';\n\n/** Hook configuration */\nexport interface UseImagegenAssistantConfig {\n /** Base URL of the imagegen-assistant service */\n baseUrl?: string;\n /** Query options for pipelines */\n pipelinesRefetchInterval?: number;\n}\n\n/**\n * Get default base URL from centralized config.\n * Uses environment variables in production, localhost in development.\n */\nconst getDefaultBaseUrl = () => getServiceUrls().imagegenAssistant;\n\n/**\n * React hook for interacting with imagegen-assistant service.\n *\n * @example\n * ```tsx\n * const { pipelines, generatePrompts, isGenerating } = useImagegenAssistant();\n *\n * const handleGenerate = async () => {\n * const result = await generatePrompts({\n * pipelineId: 'skeleton-anime-girls',\n * userInput: 'Generate 5 hologram style skeletons',\n * });\n * console.log(result.prompts);\n * };\n * ```\n */\nexport function useImagegenAssistant(config: UseImagegenAssistantConfig = {}) {\n const { baseUrl = getDefaultBaseUrl(), pipelinesRefetchInterval } = config;\n\n // Create client instance\n const client = useMemo(\n () => new ImagegenAssistantClient({ baseUrl }),\n [baseUrl],\n );\n\n // Query for health check\n const healthQuery = useQuery({\n queryKey: ['imagegen-assistant', 'health', baseUrl],\n queryFn: () => client.healthCheck(),\n refetchInterval: 30000, // Check every 30 seconds\n staleTime: 10000,\n });\n\n // Query for pipelines\n const pipelinesQuery = useQuery({\n queryKey: ['imagegen-assistant', 'pipelines', baseUrl],\n queryFn: () => client.listPipelines(),\n refetchInterval: pipelinesRefetchInterval,\n staleTime: 60000, // Pipelines don't change often\n });\n\n // Mutation for generating prompts\n const generateMutation = useMutation({\n mutationFn: (request: GeneratePromptsRequest) => client.generatePrompts(request),\n });\n\n return {\n // Health\n isHealthy: healthQuery.data?.status === 'healthy',\n ollamaAvailable: healthQuery.data?.ollamaAvailable ?? false,\n healthError: healthQuery.error,\n\n // Pipelines\n pipelines: pipelinesQuery.data ?? [],\n pipelinesLoading: pipelinesQuery.isLoading,\n pipelinesError: pipelinesQuery.error,\n\n // Generation\n generatePrompts: generateMutation.mutateAsync,\n isGenerating: generateMutation.isPending,\n generationError: generateMutation.error,\n lastGeneration: generateMutation.data,\n\n // Client instance for advanced usage\n client,\n };\n}\n\n/**\n * Hook for a single pipeline's operations\n */\nexport function usePipeline(pipelineId: string, config: UseImagegenAssistantConfig = {}) {\n const { baseUrl = getDefaultBaseUrl() } = config;\n\n const client = useMemo(\n () => new ImagegenAssistantClient({ baseUrl }),\n [baseUrl],\n );\n\n // Query for single pipeline\n const pipelineQuery = useQuery({\n queryKey: ['imagegen-assistant', 'pipeline', pipelineId, baseUrl],\n queryFn: () => client.getPipeline(pipelineId),\n enabled: !!pipelineId,\n staleTime: 60000,\n });\n\n // Mutation for generating prompts with this pipeline\n const generateMutation = useMutation({\n mutationFn: (userInput: string) =>\n client.generatePrompts({ pipelineId, userInput }),\n });\n\n return {\n pipeline: pipelineQuery.data,\n pipelineLoading: pipelineQuery.isLoading,\n pipelineError: pipelineQuery.error,\n\n generate: generateMutation.mutateAsync,\n isGenerating: generateMutation.isPending,\n generationError: generateMutation.error,\n lastGeneration: generateMutation.data,\n };\n}\n\nexport type {\n GeneratePromptsRequest,\n GeneratePromptsResponse,\n PipelineInfo,\n};\n","/**\n * Shared styled components for imagen-react\n */\n\nimport styled from 'styled-components';\n\n// ============================================================================\n// Container Components\n// ============================================================================\n\nexport const Container = styled.div`\n display: flex;\n flex-direction: column;\n height: 70vh;\n gap: 1rem;\n`;\n\nexport const Panel = styled.div`\n background: rgba(0, 0, 0, 0.3);\n border-radius: 8px;\n padding: 1rem;\n`;\n\n// ============================================================================\n// Card Components\n// ============================================================================\n\nexport const Card = styled.div`\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 12px;\n padding: 1.5rem;\n transition: all 0.2s;\n\n &:hover {\n border-color: rgba(255, 107, 180, 0.3);\n box-shadow: 0 0 20px rgba(255, 107, 180, 0.1);\n }\n`;\n\nexport const CardTitle = styled.h3`\n font-size: 1.25rem;\n font-weight: 600;\n color: #fff;\n margin-bottom: 0.5rem;\n`;\n\nexport const CardDescription = styled.p`\n color: #888;\n font-size: 0.875rem;\n margin-bottom: 1rem;\n`;\n\n// ============================================================================\n// Chat Components\n// ============================================================================\n\nexport const ChatArea = styled.div`\n flex: 1;\n overflow-y: auto;\n background: rgba(0, 0, 0, 0.3);\n border-radius: 8px;\n padding: 1rem;\n`;\n\nexport const MessageBubble = styled.div<{ $role: 'user' | 'assistant' | 'system' }>`\n max-width: 85%;\n padding: 0.75rem 1rem;\n margin-bottom: 0.75rem;\n border-radius: 12px;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n\n ${({ $role }) => {\n switch ($role) {\n case 'user':\n return `\n margin-left: auto;\n background: linear-gradient(135deg, #ff69b4, #9b59b6);\n color: #fff;\n `;\n case 'assistant':\n return `\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n `;\n case 'system':\n return `\n background: rgba(100, 116, 139, 0.2);\n color: #94a3b8;\n font-size: 0.75rem;\n text-align: center;\n max-width: 100%;\n `;\n }\n }}\n`;\n\n// ============================================================================\n// Input Components\n// ============================================================================\n\nexport const InputArea = styled.div`\n display: flex;\n gap: 0.5rem;\n`;\n\nexport const TextInput = styled.input`\n flex: 1;\n padding: 0.75rem 1rem;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: rgba(0, 0, 0, 0.3);\n color: #fff;\n font-size: 0.875rem;\n\n &::placeholder {\n color: #666;\n }\n\n &:focus {\n outline: none;\n border-color: #ff69b4;\n }\n`;\n\n// ============================================================================\n// Button Components\n// ============================================================================\n\nexport const PrimaryButton = styled.button`\n padding: 0.75rem 1.5rem;\n border-radius: 8px;\n border: none;\n background: linear-gradient(135deg, #ff69b4, #9b59b6);\n color: #fff;\n font-weight: 500;\n cursor: pointer;\n transition: opacity 0.2s;\n\n &:hover:not(:disabled) {\n opacity: 0.9;\n }\n\n &:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n`;\n\nexport const SecondaryButton = styled.button`\n padding: 0.5rem 1rem;\n border-radius: 8px;\n border: none;\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n background: rgba(255, 255, 255, 0.15);\n }\n`;\n\nexport const ExampleButton = styled.button`\n padding: 0.25rem 0.75rem;\n border-radius: 9999px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: transparent;\n color: #888;\n font-size: 0.75rem;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n border-color: #ff69b4;\n color: #ff69b4;\n }\n`;\n\n// ============================================================================\n// Badge Components\n// ============================================================================\n\nexport type BadgeVariant = 'pending' | 'generating' | 'complete' | 'failed';\n\nexport const StatBadge = styled.span<{ $variant?: BadgeVariant }>`\n padding: 0.25rem 0.75rem;\n border-radius: 9999px;\n font-size: 0.75rem;\n font-weight: 500;\n background: ${({ $variant }) => {\n switch ($variant) {\n case 'pending':\n return 'rgba(100, 116, 139, 0.2)';\n case 'generating':\n return 'rgba(234, 179, 8, 0.2)';\n case 'complete':\n return 'rgba(34, 197, 94, 0.2)';\n case 'failed':\n return 'rgba(239, 68, 68, 0.2)';\n default:\n return 'rgba(100, 116, 139, 0.2)';\n }\n }};\n color: ${({ $variant }) => {\n switch ($variant) {\n case 'pending':\n return '#94a3b8';\n case 'generating':\n return '#eab308';\n case 'complete':\n return '#22c55e';\n case 'failed':\n return '#ef4444';\n default:\n return '#94a3b8';\n }\n }};\n`;\n\n// ============================================================================\n// Status Components\n// ============================================================================\n\nexport type StatusType = 'success' | 'error' | 'info';\n\nexport const StatusMessage = styled.div<{ $type: StatusType }>`\n padding: 0.5rem 1rem;\n border-radius: 8px;\n font-size: 0.875rem;\n margin-top: 0.5rem;\n\n ${({ $type }) => {\n switch ($type) {\n case 'success':\n return `\n background: rgba(34, 197, 94, 0.2);\n color: #22c55e;\n `;\n case 'error':\n return `\n background: rgba(239, 68, 68, 0.2);\n color: #ef4444;\n `;\n case 'info':\n return `\n background: rgba(59, 130, 246, 0.2);\n color: #3b82f6;\n `;\n }\n }}\n`;\n\n// ============================================================================\n// Gallery Components\n// ============================================================================\n\nexport const ImageGrid = styled.div`\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n\n @media (max-width: 1200px) {\n grid-template-columns: repeat(3, 1fr);\n }\n\n @media (max-width: 900px) {\n grid-template-columns: repeat(2, 1fr);\n }\n`;\n\nexport const ImageCard = styled.div`\n aspect-ratio: 1;\n overflow: hidden;\n border-radius: 8px;\n cursor: pointer;\n transition: transform 0.2s;\n\n &:hover {\n transform: scale(1.02);\n }\n\n img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n`;\n\nexport const SectionTitle = styled.h2`\n font-size: 1.5rem;\n font-weight: 600;\n color: #fff;\n margin-bottom: 1rem;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n\n &::after {\n content: '';\n flex: 1;\n height: 1px;\n background: linear-gradient(90deg, rgba(255, 255, 255, 0.2), transparent);\n }\n`;\n\n// ============================================================================\n// Layout Helpers\n// ============================================================================\n\nexport const ActionBar = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 1rem;\n`;\n\nexport const ButtonRow = styled.div`\n display: flex;\n gap: 0.5rem;\n margin-top: 1rem;\n`;\n\nexport const StatsRow = styled.div`\n display: flex;\n gap: 1rem;\n margin-bottom: 1rem;\n`;\n\nexport const ExamplePrompts = styled.div`\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n margin-bottom: 0.5rem;\n`;\n","/**\n * Image Pipelines Page\n *\n * Admin interface for managing image generation pipelines.\n * Features pipeline cards with stats and ImageGen Assistant.\n */\n\nimport { useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { Grid, Stack } from '@lilith/ui-layout';\nimport { Modal, Tabs } from '@lilith/ui-feedback';\nimport { Heading, Text } from '@lilith/ui-typography';\nimport type { PipelineConfig, ParsedPrompt } from '@lilith/imajin-app';\nimport { PIPELINES, getServiceUrls } from '@lilith/imajin-app';\n\nimport { ImageGenAssistant } from '../ImageGenAssistant';\nimport {\n Card,\n CardTitle,\n CardDescription,\n StatsRow,\n StatBadge,\n ButtonRow,\n PrimaryButton,\n SecondaryButton,\n ImageGrid,\n ImageCard,\n SectionTitle,\n} from '../styles';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface QueueStats {\n pending: number;\n generating: number;\n complete: number;\n failed: number;\n total: number;\n}\n\nexport interface GalleryItem {\n src: string;\n alt: string;\n title: string;\n}\n\nexport interface ImagePipelinesPageProps {\n /** Base URL for imagegen-assistant service */\n assistantBaseUrl?: string;\n /** Custom pipelines (defaults to PIPELINES from imagen-core) */\n pipelines?: PipelineConfig[];\n /** Function to fetch queue stats for a category */\n fetchQueueStats?: (category: string) => Promise;\n /** Function to fetch gallery images for a category */\n fetchGalleryImages?: (category: string) => Promise;\n /** Function to submit prompts to the queue */\n submitPrompts?: (\n pipeline: PipelineConfig,\n prompts: ParsedPrompt[],\n ) => Promise;\n}\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\ninterface PipelineCardProps {\n pipeline: PipelineConfig;\n onOpenAssistant: () => void;\n onViewGallery: () => void;\n fetchQueueStats?: (category: string) => Promise;\n}\n\nfunction PipelineCardComponent({\n pipeline,\n onOpenAssistant,\n onViewGallery,\n fetchQueueStats,\n}: PipelineCardProps) {\n const { data: stats } = useQuery({\n queryKey: ['queue-stats', pipeline.category],\n queryFn: () => fetchQueueStats?.(pipeline.category) ?? Promise.resolve(null),\n refetchInterval: 10000,\n enabled: !!fetchQueueStats,\n });\n\n return (\n \n {pipeline.name}\n {pipeline.description}\n\n \n {stats ? (\n <>\n {stats.pending} pending\n {stats.generating} generating\n {stats.complete} complete\n {stats.failed > 0 && (\n {stats.failed} failed\n )}\n \n ) : (\n Loading...\n )}\n \n\n \n \n Open Assistant\n \n \n View Gallery\n \n \n \n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function ImagePipelinesPage({\n assistantBaseUrl = getServiceUrls().imagegenAssistant,\n pipelines = PIPELINES,\n fetchQueueStats,\n fetchGalleryImages,\n submitPrompts,\n}: ImagePipelinesPageProps) {\n const [selectedPipeline, setSelectedPipeline] = useState(null);\n const [activeTab, setActiveTab] = useState<'pipelines' | 'gallery'>('pipelines');\n const [galleryCategory, setGalleryCategory] = useState(\n pipelines[0]?.category ?? 'skeleton',\n );\n\n // Fetch gallery images\n const { data: galleryImages } = useQuery({\n queryKey: ['gallery-images', galleryCategory],\n queryFn: () => fetchGalleryImages?.(galleryCategory) ?? Promise.resolve([]),\n refetchInterval: 30000,\n enabled: !!fetchGalleryImages,\n });\n\n const galleryItems: GalleryItem[] = galleryImages ?? [];\n\n // Handle prompt submission\n const handleSubmitPrompts = async (prompts: ParsedPrompt[]) => {\n if (!selectedPipeline || !submitPrompts) return;\n await submitPrompts(selectedPipeline, prompts);\n };\n\n return (\n \n
\n \n Image Generation Pipelines\n \n \n Generate and manage images for UI components using AI-powered prompts\n \n
\n\n setActiveTab(key as 'pipelines' | 'gallery')}\n />\n\n {activeTab === 'pipelines' && (\n \n {pipelines.map((pipeline) => (\n setSelectedPipeline(pipeline)}\n onViewGallery={() => {\n setGalleryCategory(pipeline.category);\n setActiveTab('gallery');\n }}\n fetchQueueStats={fetchQueueStats}\n />\n ))}\n \n )}\n\n {activeTab === 'gallery' && (\n
\n Generated Images ({galleryItems.length})\n {galleryItems.length > 0 ? (\n \n {galleryItems.map((item, i) => (\n \n {item.alt}\n \n ))}\n \n ) : (\n \n No images generated yet. Use the Assistant to create prompts and generate images.\n \n )}\n
\n )}\n\n {/* ImageGen Assistant Modal */}\n {selectedPipeline && (\n setSelectedPipeline(null)}\n title={`ImageGen Assistant: ${selectedPipeline.name}`}\n maxWidth=\"900px\"\n maxHeight=\"90vh\"\n >\n setSelectedPipeline(null)}\n onSubmit={handleSubmitPrompts}\n />\n \n )}\n
\n );\n}\n\nexport default ImagePipelinesPage;\n","/**\n * Image Gallery Component\n *\n * Displays a grid of generated images with optional lightbox.\n */\n\nimport { useState } from 'react';\nimport { Modal } from '@lilith/ui-feedback';\nimport styled from 'styled-components';\n\nimport { ImageGrid, ImageCard, SectionTitle, CardDescription } from '../styles';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GalleryImage {\n src: string;\n alt: string;\n title?: string;\n thumbnail?: string;\n}\n\nexport interface GalleryProps {\n /** Images to display */\n images: GalleryImage[];\n /** Gallery title */\n title?: string;\n /** Enable lightbox on click */\n lightbox?: boolean;\n /** Number of columns (2-6) */\n columns?: 2 | 3 | 4 | 5 | 6;\n /** Empty state message */\n emptyMessage?: string;\n}\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst LightboxImage = styled.img`\n max-width: 100%;\n max-height: 80vh;\n object-fit: contain;\n border-radius: 8px;\n`;\n\nconst LightboxTitle = styled.h3`\n color: #fff;\n font-size: 1rem;\n font-weight: 500;\n margin-top: 1rem;\n text-align: center;\n`;\n\nconst GalleryGrid = styled(ImageGrid)<{ $columns: number }>`\n grid-template-columns: repeat(${({ $columns }) => $columns}, 1fr);\n\n @media (max-width: 1200px) {\n grid-template-columns: repeat(${({ $columns }) => Math.min($columns, 3)}, 1fr);\n }\n\n @media (max-width: 900px) {\n grid-template-columns: repeat(2, 1fr);\n }\n`;\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function Gallery({\n images,\n title,\n lightbox = true,\n columns = 4,\n emptyMessage = 'No images to display',\n}: GalleryProps) {\n const [selectedImage, setSelectedImage] = useState(null);\n\n const handleImageClick = (image: GalleryImage) => {\n if (lightbox) {\n setSelectedImage(image);\n }\n };\n\n if (images.length === 0) {\n return (\n
\n {title && {title}}\n {emptyMessage}\n
\n );\n }\n\n return (\n
\n {title && {title} ({images.length})}\n\n \n {images.map((image, i) => (\n handleImageClick(image)}\n role={lightbox ? 'button' : undefined}\n tabIndex={lightbox ? 0 : undefined}\n onKeyDown={(e) => {\n if (lightbox && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n handleImageClick(image);\n }\n }}\n >\n \n \n ))}\n \n\n {/* Lightbox Modal */}\n {lightbox && selectedImage && (\n setSelectedImage(null)}\n maxWidth=\"90vw\"\n maxHeight=\"95vh\"\n >\n \n {selectedImage.title && (\n {selectedImage.title}\n )}\n \n )}\n
\n );\n}\n\nexport default Gallery;\n","/**\n * @lilith/imajin-react\n *\n * React components for image generation UI.\n * Uses imagegen-assistant for LLM prompt generation.\n */\n\n// Components\nexport { ImageGenAssistant, type ImageGenAssistantProps } from './ImageGenAssistant';\nexport {\n ImagePipelinesPage,\n type ImagePipelinesPageProps,\n type QueueStats,\n type GalleryItem,\n} from './PipelinesPage';\nexport { Gallery, type GalleryProps, type GalleryImage } from './Gallery';\n\n// Hooks\nexport {\n useImagegenAssistant,\n usePipeline,\n type UseImagegenAssistantConfig,\n} from './hooks/useImagegenAssistant';\n\n// Re-export types from dependencies for convenience\nexport type { PipelineConfig, ParsedPrompt } from '@lilith/imajin-app';\nexport { PIPELINES, getPipelineById } from '@lilith/imajin-app';\n"],"mappings":"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","names":["getServiceUrls","styled","getServiceUrls","useState","useQuery","Stack","getServiceUrls","Fragment","jsx","jsxs","useQuery","getServiceUrls","useState","Stack","useState","Modal","styled","jsx","jsxs","styled","useState","Modal","PIPELINES"]} \ No newline at end of file diff --git a/packages/imajin-react/dist/styles.js b/packages/imajin-react/dist/styles.js deleted file mode 100644 index d95174fc..00000000 --- a/packages/imajin-react/dist/styles.js +++ /dev/null @@ -1,298 +0,0 @@ -// src/styles/index.ts -import styled from "styled-components"; -var Container = styled.div` - display: flex; - flex-direction: column; - height: 70vh; - gap: 1rem; -`; -var Panel = styled.div` - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; - padding: 1rem; -`; -var Card = styled.div` - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 12px; - padding: 1.5rem; - transition: all 0.2s; - - &:hover { - border-color: rgba(255, 107, 180, 0.3); - box-shadow: 0 0 20px rgba(255, 107, 180, 0.1); - } -`; -var CardTitle = styled.h3` - font-size: 1.25rem; - font-weight: 600; - color: #fff; - margin-bottom: 0.5rem; -`; -var CardDescription = styled.p` - color: #888; - font-size: 0.875rem; - margin-bottom: 1rem; -`; -var ChatArea = styled.div` - flex: 1; - overflow-y: auto; - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; - padding: 1rem; -`; -var MessageBubble = styled.div` - max-width: 85%; - padding: 0.75rem 1rem; - margin-bottom: 0.75rem; - border-radius: 12px; - font-size: 0.875rem; - line-height: 1.5; - white-space: pre-wrap; - - ${({ $role }) => { - switch ($role) { - case "user": - return ` - margin-left: auto; - background: linear-gradient(135deg, #ff69b4, #9b59b6); - color: #fff; - `; - case "assistant": - return ` - background: rgba(255, 255, 255, 0.1); - color: #fff; - `; - case "system": - return ` - background: rgba(100, 116, 139, 0.2); - color: #94a3b8; - font-size: 0.75rem; - text-align: center; - max-width: 100%; - `; - } -}} -`; -var InputArea = styled.div` - display: flex; - gap: 0.5rem; -`; -var TextInput = styled.input` - flex: 1; - padding: 0.75rem 1rem; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.2); - background: rgba(0, 0, 0, 0.3); - color: #fff; - font-size: 0.875rem; - - &::placeholder { - color: #666; - } - - &:focus { - outline: none; - border-color: #ff69b4; - } -`; -var PrimaryButton = styled.button` - padding: 0.75rem 1.5rem; - border-radius: 8px; - border: none; - background: linear-gradient(135deg, #ff69b4, #9b59b6); - color: #fff; - font-weight: 500; - cursor: pointer; - transition: opacity 0.2s; - - &:hover:not(:disabled) { - opacity: 0.9; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -`; -var SecondaryButton = styled.button` - padding: 0.5rem 1rem; - border-radius: 8px; - border: none; - background: rgba(255, 255, 255, 0.1); - color: #fff; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - - &:hover { - background: rgba(255, 255, 255, 0.15); - } -`; -var ExampleButton = styled.button` - padding: 0.25rem 0.75rem; - border-radius: 9999px; - border: 1px solid rgba(255, 255, 255, 0.2); - background: transparent; - color: #888; - font-size: 0.75rem; - cursor: pointer; - transition: all 0.2s; - - &:hover { - border-color: #ff69b4; - color: #ff69b4; - } -`; -var StatBadge = styled.span` - padding: 0.25rem 0.75rem; - border-radius: 9999px; - font-size: 0.75rem; - font-weight: 500; - background: ${({ $variant }) => { - switch ($variant) { - case "pending": - return "rgba(100, 116, 139, 0.2)"; - case "generating": - return "rgba(234, 179, 8, 0.2)"; - case "complete": - return "rgba(34, 197, 94, 0.2)"; - case "failed": - return "rgba(239, 68, 68, 0.2)"; - default: - return "rgba(100, 116, 139, 0.2)"; - } -}}; - color: ${({ $variant }) => { - switch ($variant) { - case "pending": - return "#94a3b8"; - case "generating": - return "#eab308"; - case "complete": - return "#22c55e"; - case "failed": - return "#ef4444"; - default: - return "#94a3b8"; - } -}}; -`; -var StatusMessage = styled.div` - padding: 0.5rem 1rem; - border-radius: 8px; - font-size: 0.875rem; - margin-top: 0.5rem; - - ${({ $type }) => { - switch ($type) { - case "success": - return ` - background: rgba(34, 197, 94, 0.2); - color: #22c55e; - `; - case "error": - return ` - background: rgba(239, 68, 68, 0.2); - color: #ef4444; - `; - case "info": - return ` - background: rgba(59, 130, 246, 0.2); - color: #3b82f6; - `; - } -}} -`; -var ImageGrid = styled.div` - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1rem; - - @media (max-width: 1200px) { - grid-template-columns: repeat(3, 1fr); - } - - @media (max-width: 900px) { - grid-template-columns: repeat(2, 1fr); - } -`; -var ImageCard = styled.div` - aspect-ratio: 1; - overflow: hidden; - border-radius: 8px; - cursor: pointer; - transition: transform 0.2s; - - &:hover { - transform: scale(1.02); - } - - img { - width: 100%; - height: 100%; - object-fit: cover; - } -`; -var SectionTitle = styled.h2` - font-size: 1.5rem; - font-weight: 600; - color: #fff; - margin-bottom: 1rem; - display: flex; - align-items: center; - gap: 0.75rem; - - &::after { - content: ''; - flex: 1; - height: 1px; - background: linear-gradient(90deg, rgba(255, 255, 255, 0.2), transparent); - } -`; -var ActionBar = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - gap: 1rem; -`; -var ButtonRow = styled.div` - display: flex; - gap: 0.5rem; - margin-top: 1rem; -`; -var StatsRow = styled.div` - display: flex; - gap: 1rem; - margin-bottom: 1rem; -`; -var ExamplePrompts = styled.div` - display: flex; - gap: 0.5rem; - flex-wrap: wrap; - margin-bottom: 0.5rem; -`; -export { - ActionBar, - ButtonRow, - Card, - CardDescription, - CardTitle, - ChatArea, - Container, - ExampleButton, - ExamplePrompts, - ImageCard, - ImageGrid, - InputArea, - MessageBubble, - Panel, - PrimaryButton, - SecondaryButton, - SectionTitle, - StatBadge, - StatsRow, - StatusMessage, - TextInput -}; -//# sourceMappingURL=styles.js.map \ No newline at end of file diff --git a/packages/imajin-react/dist/styles.js.map b/packages/imajin-react/dist/styles.js.map deleted file mode 100644 index b3f8bb5a..00000000 --- a/packages/imajin-react/dist/styles.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/styles/index.ts"],"sourcesContent":["/**\n * Shared styled components for imagen-react\n */\n\nimport styled from 'styled-components';\n\n// ============================================================================\n// Container Components\n// ============================================================================\n\nexport const Container = styled.div`\n display: flex;\n flex-direction: column;\n height: 70vh;\n gap: 1rem;\n`;\n\nexport const Panel = styled.div`\n background: rgba(0, 0, 0, 0.3);\n border-radius: 8px;\n padding: 1rem;\n`;\n\n// ============================================================================\n// Card Components\n// ============================================================================\n\nexport const Card = styled.div`\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 12px;\n padding: 1.5rem;\n transition: all 0.2s;\n\n &:hover {\n border-color: rgba(255, 107, 180, 0.3);\n box-shadow: 0 0 20px rgba(255, 107, 180, 0.1);\n }\n`;\n\nexport const CardTitle = styled.h3`\n font-size: 1.25rem;\n font-weight: 600;\n color: #fff;\n margin-bottom: 0.5rem;\n`;\n\nexport const CardDescription = styled.p`\n color: #888;\n font-size: 0.875rem;\n margin-bottom: 1rem;\n`;\n\n// ============================================================================\n// Chat Components\n// ============================================================================\n\nexport const ChatArea = styled.div`\n flex: 1;\n overflow-y: auto;\n background: rgba(0, 0, 0, 0.3);\n border-radius: 8px;\n padding: 1rem;\n`;\n\nexport const MessageBubble = styled.div<{ $role: 'user' | 'assistant' | 'system' }>`\n max-width: 85%;\n padding: 0.75rem 1rem;\n margin-bottom: 0.75rem;\n border-radius: 12px;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n\n ${({ $role }) => {\n switch ($role) {\n case 'user':\n return `\n margin-left: auto;\n background: linear-gradient(135deg, #ff69b4, #9b59b6);\n color: #fff;\n `;\n case 'assistant':\n return `\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n `;\n case 'system':\n return `\n background: rgba(100, 116, 139, 0.2);\n color: #94a3b8;\n font-size: 0.75rem;\n text-align: center;\n max-width: 100%;\n `;\n }\n }}\n`;\n\n// ============================================================================\n// Input Components\n// ============================================================================\n\nexport const InputArea = styled.div`\n display: flex;\n gap: 0.5rem;\n`;\n\nexport const TextInput = styled.input`\n flex: 1;\n padding: 0.75rem 1rem;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: rgba(0, 0, 0, 0.3);\n color: #fff;\n font-size: 0.875rem;\n\n &::placeholder {\n color: #666;\n }\n\n &:focus {\n outline: none;\n border-color: #ff69b4;\n }\n`;\n\n// ============================================================================\n// Button Components\n// ============================================================================\n\nexport const PrimaryButton = styled.button`\n padding: 0.75rem 1.5rem;\n border-radius: 8px;\n border: none;\n background: linear-gradient(135deg, #ff69b4, #9b59b6);\n color: #fff;\n font-weight: 500;\n cursor: pointer;\n transition: opacity 0.2s;\n\n &:hover:not(:disabled) {\n opacity: 0.9;\n }\n\n &:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n`;\n\nexport const SecondaryButton = styled.button`\n padding: 0.5rem 1rem;\n border-radius: 8px;\n border: none;\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n background: rgba(255, 255, 255, 0.15);\n }\n`;\n\nexport const ExampleButton = styled.button`\n padding: 0.25rem 0.75rem;\n border-radius: 9999px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: transparent;\n color: #888;\n font-size: 0.75rem;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n border-color: #ff69b4;\n color: #ff69b4;\n }\n`;\n\n// ============================================================================\n// Badge Components\n// ============================================================================\n\nexport type BadgeVariant = 'pending' | 'generating' | 'complete' | 'failed';\n\nexport const StatBadge = styled.span<{ $variant?: BadgeVariant }>`\n padding: 0.25rem 0.75rem;\n border-radius: 9999px;\n font-size: 0.75rem;\n font-weight: 500;\n background: ${({ $variant }) => {\n switch ($variant) {\n case 'pending':\n return 'rgba(100, 116, 139, 0.2)';\n case 'generating':\n return 'rgba(234, 179, 8, 0.2)';\n case 'complete':\n return 'rgba(34, 197, 94, 0.2)';\n case 'failed':\n return 'rgba(239, 68, 68, 0.2)';\n default:\n return 'rgba(100, 116, 139, 0.2)';\n }\n }};\n color: ${({ $variant }) => {\n switch ($variant) {\n case 'pending':\n return '#94a3b8';\n case 'generating':\n return '#eab308';\n case 'complete':\n return '#22c55e';\n case 'failed':\n return '#ef4444';\n default:\n return '#94a3b8';\n }\n }};\n`;\n\n// ============================================================================\n// Status Components\n// ============================================================================\n\nexport type StatusType = 'success' | 'error' | 'info';\n\nexport const StatusMessage = styled.div<{ $type: StatusType }>`\n padding: 0.5rem 1rem;\n border-radius: 8px;\n font-size: 0.875rem;\n margin-top: 0.5rem;\n\n ${({ $type }) => {\n switch ($type) {\n case 'success':\n return `\n background: rgba(34, 197, 94, 0.2);\n color: #22c55e;\n `;\n case 'error':\n return `\n background: rgba(239, 68, 68, 0.2);\n color: #ef4444;\n `;\n case 'info':\n return `\n background: rgba(59, 130, 246, 0.2);\n color: #3b82f6;\n `;\n }\n }}\n`;\n\n// ============================================================================\n// Gallery Components\n// ============================================================================\n\nexport const ImageGrid = styled.div`\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n\n @media (max-width: 1200px) {\n grid-template-columns: repeat(3, 1fr);\n }\n\n @media (max-width: 900px) {\n grid-template-columns: repeat(2, 1fr);\n }\n`;\n\nexport const ImageCard = styled.div`\n aspect-ratio: 1;\n overflow: hidden;\n border-radius: 8px;\n cursor: pointer;\n transition: transform 0.2s;\n\n &:hover {\n transform: scale(1.02);\n }\n\n img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n`;\n\nexport const SectionTitle = styled.h2`\n font-size: 1.5rem;\n font-weight: 600;\n color: #fff;\n margin-bottom: 1rem;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n\n &::after {\n content: '';\n flex: 1;\n height: 1px;\n background: linear-gradient(90deg, rgba(255, 255, 255, 0.2), transparent);\n }\n`;\n\n// ============================================================================\n// Layout Helpers\n// ============================================================================\n\nexport const ActionBar = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 1rem;\n`;\n\nexport const ButtonRow = styled.div`\n display: flex;\n gap: 0.5rem;\n margin-top: 1rem;\n`;\n\nexport const StatsRow = styled.div`\n display: flex;\n gap: 1rem;\n margin-bottom: 1rem;\n`;\n\nexport const ExamplePrompts = styled.div`\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n margin-bottom: 0.5rem;\n`;\n"],"mappings":"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","names":[]} \ No newline at end of file