Spaces:
Sleeping
Sleeping
Sync from enhance: d795216 feat: implement patch response parser for search and replace functionality
9bd4242 | // AI 修改对话框 | |
| import { useState } from 'react'; | |
| import { useI18n } from '../i18n'; | |
| import { useModalTransition } from '../hooks/useModalTransition'; | |
| interface AiModifyModalProps { | |
| isOpen: boolean; | |
| loading?: boolean; | |
| onClose: () => void; | |
| onSubmit: (value: string) => void; | |
| } | |
| export function AiModifyModal({ isOpen, loading = false, onClose, onSubmit }: AiModifyModalProps) { | |
| const { t } = useI18n(); | |
| const { shouldRender, isExiting } = useModalTransition(isOpen); | |
| const [draft, setDraft] = useState(''); | |
| if (!shouldRender) return null; | |
| return ( | |
| <div className="fixed inset-0 z-[120] flex items-center justify-center p-4"> | |
| {/* 沉浸式背景 */} | |
| <div | |
| className={`absolute inset-0 bg-bg-primary/60 backdrop-blur-md transition-opacity duration-300 ${ | |
| isExiting ? 'opacity-0' : 'animate-overlay-wash-in' | |
| }`} | |
| onClick={onClose} | |
| /> | |
| {/* 模态框主体 */} | |
| <div className={`relative w-full max-w-lg bg-bg-secondary rounded-[2.5rem] p-10 shadow-2xl border border-border/5 ${ | |
| isExiting ? 'animate-fade-out-soft' : 'animate-fade-in-soft' | |
| }`}> | |
| <div className="flex items-center justify-between mb-8"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-2 h-2 rounded-full bg-accent-rgb/40 animate-pulse" /> | |
| <h2 className="text-xl font-medium text-text-primary tracking-tight">{t('aiModify.title')}</h2> | |
| </div> | |
| <button | |
| onClick={onClose} | |
| className="p-2.5 text-text-secondary/50 hover:text-text-primary hover:bg-bg-primary/50 rounded-2xl transition-all" | |
| > | |
| <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <p className="text-text-secondary text-[15px] mb-8 leading-relaxed font-light"> | |
| {t('aiModify.description')} | |
| </p> | |
| <div className="relative mb-10 group"> | |
| <textarea | |
| id="aiModifyInput" | |
| rows={5} | |
| value={draft} | |
| onChange={(e) => setDraft(e.target.value)} | |
| placeholder={t('aiModify.placeholder')} | |
| className="w-full px-6 py-6 bg-bg-secondary/50 border border-border/5 rounded-3xl text-base text-text-primary placeholder-text-secondary/30 focus:outline-none focus:border-accent-rgb/30 focus:bg-bg-secondary/80 transition-all resize-none shadow-inner" | |
| /> | |
| <label | |
| htmlFor="aiModifyInput" | |
| className="absolute right-6 -bottom-3 px-3 py-1 bg-bg-secondary border border-border/5 rounded-full text-[10px] uppercase tracking-widest text-text-secondary/40 group-focus-within:text-accent-rgb/60 group-focus-within:border-accent-rgb/20 transition-all" | |
| > | |
| {t('aiModify.label')} | |
| </label> | |
| </div> | |
| <div className="flex gap-4"> | |
| <button | |
| onClick={onClose} | |
| disabled={loading} | |
| className="flex-1 py-4 text-sm text-text-secondary hover:text-text-primary bg-bg-primary/50 hover:bg-bg-tertiary rounded-2xl transition-all active:scale-95 disabled:opacity-50" | |
| > | |
| {t('common.cancel')} | |
| </button> | |
| <button | |
| onClick={() => onSubmit(draft.trim())} | |
| disabled={loading || draft.trim().length === 0} | |
| className="flex-1 py-4 text-sm text-bg-primary bg-text-primary hover:bg-accent-hover-rgb rounded-2xl transition-all active:scale-95 disabled:opacity-50 shadow-lg font-medium" | |
| > | |
| {loading ? t('aiModify.submitting') : t('aiModify.submit')} | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |