diff --git a/frontend/apps/artcraft-website/src/lib/cost-estimate-api.ts b/frontend/apps/artcraft-website/src/lib/cost-estimate-api.ts index ffacecb6c0..e1e425288f 100644 --- a/frontend/apps/artcraft-website/src/lib/cost-estimate-api.ts +++ b/frontend/apps/artcraft-website/src/lib/cost-estimate-api.ts @@ -8,6 +8,7 @@ export interface ImageCostParams { model: string; aspectRatio?: string; resolution?: string; + quality?: string; numImages: number; hasReferenceImages: boolean; imageMediaTokenCount?: number; @@ -29,6 +30,7 @@ export function useImageCostEstimate(params: ImageCostParams): number | null { model: params.model, aspect_ratio: params.aspectRatio ?? null, resolution: params.resolution ?? null, + quality: params.quality ?? null, image_batch_count: params.numImages, image_media_tokens: params.hasReferenceImages ? new Array(params.imageMediaTokenCount ?? 1).fill("placeholder") diff --git a/frontend/apps/artcraft-website/src/pages/create-image/components/QualityPicker.tsx b/frontend/apps/artcraft-website/src/pages/create-image/components/QualityPicker.tsx new file mode 100644 index 0000000000..c136ec8289 --- /dev/null +++ b/frontend/apps/artcraft-website/src/pages/create-image/components/QualityPicker.tsx @@ -0,0 +1,59 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGem } from "@fortawesome/pro-solid-svg-icons"; +import { PopoverItem, PopoverMenu } from "@storyteller/ui-popover"; +import { Tooltip } from "@storyteller/ui-tooltip"; + +interface QualityPickerProps { + qualityOptions: string[]; + defaultQuality?: string; + currentQuality?: string; + handleQualitySelect: (selected: string) => void; +} + +const QUALITY_LABELS: Record = { + high: "High", + medium: "Medium", + low: "Low", +}; + +const LABEL_TO_QUALITY: Record = Object.fromEntries( + Object.entries(QUALITY_LABELS).map(([k, v]) => [v, k]), +); + +export const QualityPicker = ({ + qualityOptions, + defaultQuality, + currentQuality, + handleQualitySelect, +}: QualityPickerProps) => { + const activeQuality = currentQuality ?? defaultQuality ?? undefined; + + const handleSelectAdapter = (item: PopoverItem) => { + const quality = LABEL_TO_QUALITY[item.label]; + if (quality) handleQualitySelect(quality); + }; + + const qualityList: PopoverItem[] = qualityOptions.map((q) => ({ + label: QUALITY_LABELS[q] ?? q, + selected: activeQuality === q, + })); + + return ( + + + } + /> + + ); +}; diff --git a/frontend/apps/artcraft-website/src/pages/create-image/create-image-store.ts b/frontend/apps/artcraft-website/src/pages/create-image/create-image-store.ts index 43a63ff4a3..cf1bac7727 100644 --- a/frontend/apps/artcraft-website/src/pages/create-image/create-image-store.ts +++ b/frontend/apps/artcraft-website/src/pages/create-image/create-image-store.ts @@ -24,6 +24,7 @@ export type ImageUiState = { aspectRatio: string; numImages: number; resolution: string | undefined; + quality: string | undefined; }; type CreateImageState = { @@ -49,6 +50,7 @@ const DEFAULT_UI: ImageUiState = { aspectRatio: "square", numImages: 1, resolution: undefined, + quality: undefined, }; export const useCreateImageStore = create((set) => ({ diff --git a/frontend/apps/artcraft-website/src/pages/create-image/create-image.tsx b/frontend/apps/artcraft-website/src/pages/create-image/create-image.tsx index 78f495c0dc..741a739cf1 100644 --- a/frontend/apps/artcraft-website/src/pages/create-image/create-image.tsx +++ b/frontend/apps/artcraft-website/src/pages/create-image/create-image.tsx @@ -23,6 +23,7 @@ import { enqueueImageGeneration, startPolling } from "./generate-image-api"; import { AspectRatioPicker } from "./components/AspectRatioPicker"; import { GenerationCountPicker } from "./components/GenerationCountPicker"; import { ResolutionPicker } from "./components/ResolutionPicker"; +import { QualityPicker } from "./components/QualityPicker"; import { useImageCostEstimate } from "../../lib/cost-estimate-api"; import { useOmniGenImageModels, @@ -100,6 +101,11 @@ export default function CreateImage() { (v: string | undefined) => setUi({ resolution: v }), [setUi], ); + const quality = ui.quality; + const setQuality = useCallback( + (v: string | undefined) => setUi({ quality: v }), + [setUi], + ); const [isGenerating, setIsGenerating] = useState(false); const [referenceImages, setReferenceImages] = useState([]); @@ -138,11 +144,13 @@ export default function CreateImage() { const hasAspectRatios = (selectedModel?.aspect_ratio_options?.length ?? 0) > 0; const hasResolutions = (selectedModel?.resolution_options?.length ?? 0) > 0; + const hasQualityOptions = (selectedModel?.quality_options?.length ?? 0) > 0; const estimatedCredits = useImageCostEstimate({ model: selectedModel?.model ?? "", aspectRatio: aspectRatio, resolution: hasResolutions ? resolution : undefined, + quality: hasQualityOptions ? quality : undefined, numImages, hasReferenceImages: referenceImages.length > 0, }); @@ -206,6 +214,7 @@ export default function CreateImage() { model.batch_size_default ?? 1, ), resolution: model.resolution_default ?? undefined, + quality: model.default_quality ?? undefined, }); }, [setUi], @@ -251,6 +260,7 @@ export default function CreateImage() { numImages, aspectRatio: aspectRatio, resolution: hasResolutions ? resolution : undefined, + quality: hasQualityOptions ? quality : undefined, imageMediaTokens: imageMediaTokens?.length ? imageMediaTokens : undefined, @@ -293,6 +303,8 @@ export default function CreateImage() { aspectRatio, resolution, hasResolutions, + quality, + hasQualityOptions, referenceImages, startBatch, setBatchJobToken, @@ -373,6 +385,16 @@ export default function CreateImage() { handleResolutionSelect={setResolution} /> )} + {hasQualityOptions && selectedModel && ( + + )} } rightToolbar={ diff --git a/frontend/apps/artcraft-website/src/pages/create-image/generate-image-api.ts b/frontend/apps/artcraft-website/src/pages/create-image/generate-image-api.ts index db9b5cc2ce..1971ba218c 100644 --- a/frontend/apps/artcraft-website/src/pages/create-image/generate-image-api.ts +++ b/frontend/apps/artcraft-website/src/pages/create-image/generate-image-api.ts @@ -10,6 +10,7 @@ export interface GenerateImageParams { numImages?: number; aspectRatio?: string; resolution?: string; + quality?: string; imageMediaTokens?: string[]; } @@ -24,6 +25,7 @@ export async function enqueueImageGeneration( idempotency_token: crypto.randomUUID(), aspect_ratio: params.aspectRatio ?? null, resolution: params.resolution ?? null, + quality: params.quality ?? null, image_batch_count: params.numImages ?? 1, image_media_tokens: params.imageMediaTokens?.length ? params.imageMediaTokens diff --git a/frontend/apps/artcraft-website/src/pages/pricing/pricing.tsx b/frontend/apps/artcraft-website/src/pages/pricing/pricing.tsx index 0f211414e5..ab8b77d339 100644 --- a/frontend/apps/artcraft-website/src/pages/pricing/pricing.tsx +++ b/frontend/apps/artcraft-website/src/pages/pricing/pricing.tsx @@ -92,7 +92,7 @@ const Pricing = () => { ) : ( -
+