Fix prop confirmation bug, add Wan 2.7 model, refine multiple UI details, improve prop generation quality and aspect ratio, remove text overlays from Asset Center created images, and optimize prop filtering logic
This commit is contained in:
@@ -3,15 +3,22 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { AppIcon } from '@/components/ui/icons'
|
||||
import { shouldShowError } from '@/lib/error-utils'
|
||||
import TaskStatusInline from '@/components/task/TaskStatusInline'
|
||||
import { resolveTaskPresentationState } from '@/lib/task/presentation'
|
||||
import { useAssetActions } from '@/lib/query/hooks'
|
||||
import {
|
||||
useAiModifyProjectPropDescription,
|
||||
useAiModifyPropDescription,
|
||||
useAssetActions,
|
||||
} from '@/lib/query/hooks'
|
||||
import { AiModifyDescriptionField } from './AiModifyDescriptionField'
|
||||
|
||||
export interface PropEditModalProps {
|
||||
mode: 'asset-hub' | 'project'
|
||||
propId: string
|
||||
propName: string
|
||||
summary: string
|
||||
description: string
|
||||
variantId?: string
|
||||
projectId?: string
|
||||
onClose: () => void
|
||||
@@ -23,6 +30,7 @@ export function PropEditModal({
|
||||
propId,
|
||||
propName,
|
||||
summary,
|
||||
description,
|
||||
variantId,
|
||||
projectId,
|
||||
onClose,
|
||||
@@ -36,7 +44,18 @@ export function PropEditModal({
|
||||
})
|
||||
const [editingName, setEditingName] = useState(propName)
|
||||
const [editingSummary, setEditingSummary] = useState(summary)
|
||||
const [editingDescription, setEditingDescription] = useState(description)
|
||||
const [aiModifyInstruction, setAiModifyInstruction] = useState('')
|
||||
const [isAiModifying, setIsAiModifying] = useState(false)
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const aiModifyingState = isAiModifying
|
||||
? resolveTaskPresentationState({
|
||||
phase: 'processing',
|
||||
intent: 'modify',
|
||||
resource: 'image',
|
||||
hasOutput: true,
|
||||
})
|
||||
: null
|
||||
const savingState = isSaving
|
||||
? resolveTaskPresentationState({
|
||||
phase: 'processing',
|
||||
@@ -45,6 +64,13 @@ export function PropEditModal({
|
||||
hasOutput: false,
|
||||
})
|
||||
: null
|
||||
const aiModifyAssetHub = useAiModifyPropDescription()
|
||||
const aiModifyProject = useAiModifyProjectPropDescription(projectId ?? '')
|
||||
|
||||
const getErrorMessage = (error: unknown, fallback: string) => {
|
||||
if (error instanceof Error && error.message) return error.message
|
||||
return fallback
|
||||
}
|
||||
|
||||
const persist = async () => {
|
||||
await actions.update(propId, {
|
||||
@@ -53,14 +79,49 @@ export function PropEditModal({
|
||||
})
|
||||
if (variantId) {
|
||||
await actions.updateVariant(propId, variantId, {
|
||||
description: editingSummary.trim(),
|
||||
description: editingDescription.trim(),
|
||||
})
|
||||
}
|
||||
onRefresh?.()
|
||||
}
|
||||
|
||||
const handleAiModify = async () => {
|
||||
if (!aiModifyInstruction.trim()) return false
|
||||
|
||||
try {
|
||||
setIsAiModifying(true)
|
||||
const data = mode === 'asset-hub'
|
||||
? await aiModifyAssetHub.mutateAsync({
|
||||
propId,
|
||||
variantId,
|
||||
currentDescription: editingDescription,
|
||||
modifyInstruction: aiModifyInstruction,
|
||||
})
|
||||
: await aiModifyProject.mutateAsync({
|
||||
propId,
|
||||
variantId,
|
||||
currentDescription: editingDescription,
|
||||
modifyInstruction: aiModifyInstruction,
|
||||
})
|
||||
|
||||
if (data?.modifiedDescription) {
|
||||
setEditingDescription(data.modifiedDescription)
|
||||
setAiModifyInstruction('')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} catch (error: unknown) {
|
||||
if (shouldShowError(error)) {
|
||||
alert(`${t('modal.modifyFailed')}: ${getErrorMessage(error, t('errors.failed'))}`)
|
||||
}
|
||||
return false
|
||||
} finally {
|
||||
setIsAiModifying(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveOnly = async () => {
|
||||
if (!editingName.trim() || !editingSummary.trim()) return
|
||||
if (!editingName.trim() || !editingSummary.trim() || !editingDescription.trim()) return
|
||||
try {
|
||||
setIsSaving(true)
|
||||
await persist()
|
||||
@@ -71,7 +132,7 @@ export function PropEditModal({
|
||||
}
|
||||
|
||||
const handleSaveAndGenerate = async () => {
|
||||
if (!editingName.trim() || !editingSummary.trim()) return
|
||||
if (!editingName.trim() || !editingSummary.trim() || !editingDescription.trim()) return
|
||||
try {
|
||||
setIsSaving(true)
|
||||
await persist()
|
||||
@@ -118,10 +179,25 @@ export function PropEditModal({
|
||||
<textarea
|
||||
value={editingSummary}
|
||||
onChange={(event) => setEditingSummary(event.target.value)}
|
||||
className="glass-textarea-base w-full h-48 px-3 py-2 resize-none"
|
||||
className="glass-textarea-base h-28 w-full px-3 py-2 resize-none"
|
||||
placeholder={t('prop.summaryPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AiModifyDescriptionField
|
||||
label={t('prop.description')}
|
||||
description={editingDescription}
|
||||
onDescriptionChange={setEditingDescription}
|
||||
descriptionPlaceholder={t('prop.descriptionPlaceholder')}
|
||||
aiInstruction={aiModifyInstruction}
|
||||
onAiInstructionChange={setAiModifyInstruction}
|
||||
aiInstructionPlaceholder={t('modal.modifyPlaceholderProp')}
|
||||
onAiModify={handleAiModify}
|
||||
isAiModifying={isAiModifying}
|
||||
aiModifyingState={aiModifyingState}
|
||||
actionLabel={t('modal.modifyDescription')}
|
||||
cancelLabel={t('common.cancel')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 justify-end p-4 border-t border-[var(--glass-stroke-base)] bg-[var(--glass-bg-surface-strong)] rounded-b-lg flex-shrink-0">
|
||||
@@ -134,7 +210,7 @@ export function PropEditModal({
|
||||
</button>
|
||||
<button
|
||||
onClick={() => void handleSaveOnly()}
|
||||
disabled={isSaving || !editingName.trim() || !editingSummary.trim()}
|
||||
disabled={isSaving || !editingName.trim() || !editingSummary.trim() || !editingDescription.trim()}
|
||||
className="glass-btn-base glass-btn-tone-info px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
>
|
||||
{isSaving ? (
|
||||
@@ -145,7 +221,7 @@ export function PropEditModal({
|
||||
</button>
|
||||
<button
|
||||
onClick={() => void handleSaveAndGenerate()}
|
||||
disabled={isSaving || !editingName.trim() || !editingSummary.trim()}
|
||||
disabled={isSaving || !editingName.trim() || !editingSummary.trim() || !editingDescription.trim()}
|
||||
className="glass-btn-base glass-btn-primary px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t('modal.saveAndGenerate')}
|
||||
|
||||
Reference in New Issue
Block a user