Fix image generation count selector
This commit is contained in:
@@ -45,6 +45,7 @@ Use this guide when the user reports behavior. Start from the symptom row, inspe
|
||||
| Reference image upload too large or fails | `src/components/create/image-to-image.tsx`, `src/components/create/image-to-video.tsx`, `src/lib/browser-image-compression.ts`, `src/lib/server-image-compression.ts`, `src/app/api/generate/image/route.ts`, `src/app/api/generate/video/route.ts` | Browser compression, `MAX_UPSTREAM_REFERENCE_IMAGE_BYTES`, data URL conversion. |
|
||||
| Generated result previews but does not persist | `src/app/api/generate/image/route.ts`, `src/app/api/generate/video/route.ts`, `src/lib/local-storage.ts`, `src/app/api/creation-history/route.ts` | Media copied to local storage, presigned URL returned, history POST called. |
|
||||
| Fullscreen/preview/download broken | `src/components/fullscreen-preview.tsx`, `src/components/lightbox.tsx`, `src/components/creation-detail-dialog.tsx`, `src/app/api/download/route.ts` | Dialog state, URL type, download proxy supports local/remote URL. |
|
||||
| Image generation count dropdown too wide or options missing | `src/components/create/text-to-image.tsx`, `src/components/create/image-to-image.tsx` | Use the shared Radix `Select` pattern instead of browser `datalist`; keep the trigger narrow and verify options render in both text-to-image and image-to-image panels. |
|
||||
| Reverse prompt option missing | `src/components/create/reverse-prompt-panel.tsx`, `src/app/api/generate/reverse-prompt/route.ts` | UI option list and server `outputMode` handling both updated, app rebuilt/restarted if deployed. |
|
||||
| Prompt optimization fails | `src/app/api/generate/suggest-prompt/route.ts`, `src/lib/server-api-config.ts`, `src/lib/custom-api-fetch.ts` | Text-capable system/custom API, chat response shape, JSON parsing fallback. |
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { useState, useCallback, useRef, useMemo, useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Slider } from '@/components/ui/slider';
|
||||
@@ -49,19 +48,13 @@ import { compressImageFileForUpload } from '@/lib/browser-image-compression';
|
||||
import { StylePresetSelector } from '@/components/create/style-preset-selector';
|
||||
|
||||
const IMAGE_TO_IMAGE_DRAFT_KEY = 'miaojing:image-to-image-draft';
|
||||
const IMAGE_COUNT_DATALIST_OPTIONS = ['自动', '1', '2', '4'] as const;
|
||||
|
||||
function toCountInputValue(value: string): string {
|
||||
return value === 'auto' ? '自动' : value;
|
||||
}
|
||||
|
||||
function normalizeCountInputValue(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed || trimmed === '自动' || trimmed.toLowerCase() === 'auto') return 'auto';
|
||||
const numeric = trimmed.match(/\d+/)?.[0];
|
||||
if (!numeric) return trimmed;
|
||||
return String(Math.min(10, Math.max(1, Math.floor(Number(numeric)))));
|
||||
}
|
||||
const IMAGE_COUNT_OPTIONS = [
|
||||
{ value: 'auto', label: '自动' },
|
||||
{ value: '1', label: '1 张' },
|
||||
{ value: '2', label: '2 张' },
|
||||
{ value: '3', label: '3 张' },
|
||||
{ value: '4', label: '4 张' },
|
||||
] as const;
|
||||
|
||||
interface RefImage {
|
||||
id: string;
|
||||
@@ -661,20 +654,19 @@ export function ImageToImagePanel() {
|
||||
|
||||
{/* Count */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="image-to-image-count">生成数量</Label>
|
||||
<Input
|
||||
id="image-to-image-count"
|
||||
list="image-to-image-count-options"
|
||||
inputMode="numeric"
|
||||
value={toCountInputValue(count)}
|
||||
onChange={e => setCount(normalizeCountInputValue(e.target.value))}
|
||||
placeholder="自动"
|
||||
/>
|
||||
<datalist id="image-to-image-count-options">
|
||||
{IMAGE_COUNT_DATALIST_OPTIONS.map(option => (
|
||||
<option key={option} value={option} />
|
||||
))}
|
||||
</datalist>
|
||||
<Label>生成数量</Label>
|
||||
<Select value={count} onValueChange={setCount}>
|
||||
<SelectTrigger className="w-28">
|
||||
<SelectValue placeholder="自动" />
|
||||
</SelectTrigger>
|
||||
<SelectContent align="start" className="min-w-28">
|
||||
{IMAGE_COUNT_OPTIONS.map(option => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Generate */}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { useState, useCallback, useRef, useMemo, useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Slider } from '@/components/ui/slider';
|
||||
@@ -47,19 +46,13 @@ import { GenerationLoadingPanel } from '@/components/create/generation-loading-p
|
||||
import { StylePresetSelector } from '@/components/create/style-preset-selector';
|
||||
|
||||
const TEXT_TO_IMAGE_DRAFT_KEY = 'miaojing:text-to-image-draft';
|
||||
const IMAGE_COUNT_DATALIST_OPTIONS = ['自动', '1', '2', '4'] as const;
|
||||
|
||||
function toCountInputValue(value: string): string {
|
||||
return value === 'auto' ? '自动' : value;
|
||||
}
|
||||
|
||||
function normalizeCountInputValue(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed || trimmed === '自动' || trimmed.toLowerCase() === 'auto') return 'auto';
|
||||
const numeric = trimmed.match(/\d+/)?.[0];
|
||||
if (!numeric) return trimmed;
|
||||
return String(Math.min(10, Math.max(1, Math.floor(Number(numeric)))));
|
||||
}
|
||||
const IMAGE_COUNT_OPTIONS = [
|
||||
{ value: 'auto', label: '自动' },
|
||||
{ value: '1', label: '1 张' },
|
||||
{ value: '2', label: '2 张' },
|
||||
{ value: '3', label: '3 张' },
|
||||
{ value: '4', label: '4 张' },
|
||||
] as const;
|
||||
|
||||
export function TextToImagePanel() {
|
||||
const { user, accessToken } = useAuth();
|
||||
@@ -481,20 +474,19 @@ export function TextToImagePanel() {
|
||||
|
||||
{/* Count */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="text-to-image-count">生成数量</Label>
|
||||
<Input
|
||||
id="text-to-image-count"
|
||||
list="text-to-image-count-options"
|
||||
inputMode="numeric"
|
||||
value={toCountInputValue(count)}
|
||||
onChange={e => setCount(normalizeCountInputValue(e.target.value))}
|
||||
placeholder="自动"
|
||||
/>
|
||||
<datalist id="text-to-image-count-options">
|
||||
{IMAGE_COUNT_DATALIST_OPTIONS.map(option => (
|
||||
<option key={option} value={option} />
|
||||
))}
|
||||
</datalist>
|
||||
<Label>生成数量</Label>
|
||||
<Select value={count} onValueChange={setCount}>
|
||||
<SelectTrigger className="w-28">
|
||||
<SelectValue placeholder="自动" />
|
||||
</SelectTrigger>
|
||||
<SelectContent align="start" className="min-w-28">
|
||||
{IMAGE_COUNT_OPTIONS.map(option => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Generate Button */}
|
||||
|
||||
Reference in New Issue
Block a user