added quality selector for image models, pricing page mobile padding fix

This commit is contained in:
bflatastic committed 2026-04-11 00:33:42 +07:00
1 parent a1072b7eb7
commit 7e863ed872
6 files changed
+88 -1

No files matched your search

@@ -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")
@@ -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<string, string> = {
high: "High",
medium: "Medium",
low: "Low",
};
const LABEL_TO_QUALITY: Record<string, string> = 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 (
<Tooltip
content="Quality"
position="top"
className="z-50"
closeOnClick={true}
>
<PopoverMenu
items={qualityList}
onSelect={handleSelectAdapter}
mode="toggle"
panelTitle="Quality"
triggerIcon={
<FontAwesomeIcon icon={faGem} className="h-3.5 w-3.5" />
}
/>
</Tooltip>
);
};
@@ -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<CreateImageState>((set) => ({
@@ -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<RefImage[]>([]);
@@ -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 && (
<QualityPicker
qualityOptions={selectedModel.quality_options ?? []}
defaultQuality={
selectedModel.default_quality ?? undefined
}
currentQuality={quality}
handleQualitySelect={setQuality}
/>
)}
</>
}
rightToolbar={
@@ -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
@@ -92,7 +92,7 @@ const Pricing = () => {
</div>
</main>
) : (
<main className="relative z-10 pt-32 pb-20 px-4 sm:px-6 lg:px-8">
<main className="relative z-10 pt-28 sm:pt-32 pb-20 px-4 sm:px-6 lg:px-8">
<PricingTable
title="Invest in Yourself"
subtitle="You'll get a ton of generations and you'll be investing in a tool that you'll always own."