feat: refine UI, improve UX, optimize the analysis pipeline, and add character standing positions

This commit is contained in:
saturn
2026-04-02 17:39:16 +08:00
parent c3e74c228a
commit 9703714b69
153 changed files with 4472 additions and 1088 deletions

View File

@@ -0,0 +1,124 @@
'use client'
import { useEffect } from 'react'
import { createPortal } from 'react-dom'
import { AppIcon } from '@/components/ui/icons'
interface LongTextDetectionPromptCopy {
title: string
description: string
strongRecommend: string
smartSplitLabel: string
smartSplitBadge: string
continueLabel: string
continueHint: string
}
interface LongTextDetectionPromptProps {
open: boolean
copy: LongTextDetectionPromptCopy
onClose: () => void
onSmartSplit: () => void
onContinue: () => void
}
export default function LongTextDetectionPrompt({
open,
copy,
onClose,
onSmartSplit,
onContinue,
}: LongTextDetectionPromptProps) {
useEffect(() => {
if (!open) return
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onClose()
}
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [onClose, open])
if (!open || typeof document === 'undefined') {
return null
}
return createPortal(
<div
className="fixed inset-0 z-[120] flex items-center justify-center glass-overlay p-4 backdrop-blur-sm"
role="dialog"
aria-modal="true"
onMouseDown={(event) => {
if (event.target === event.currentTarget) {
onClose()
}
}}
>
<div className="glass-surface-modal w-full max-w-lg rounded-2xl border border-[var(--glass-stroke-base)] p-6 shadow-[0_20px_80px_-32px_rgba(15,23,42,0.45)]">
<div className="space-y-5">
<div className="flex items-center gap-3">
<div
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl"
style={{ background: 'linear-gradient(135deg, rgba(59,130,246,0.15), rgba(139,92,246,0.15))' }}
>
<AppIcon name="sparkles" className="h-5 w-5 text-[#7c3aed]" />
</div>
<h3 className="text-lg font-bold text-[var(--glass-text-primary)]">
{copy.title}
</h3>
</div>
<p className="text-sm leading-relaxed text-[var(--glass-text-secondary)]">
{copy.description}
</p>
<div
className="rounded-xl p-4 text-sm leading-relaxed"
style={{ background: 'linear-gradient(135deg, rgba(59,130,246,0.08), rgba(139,92,246,0.08))' }}
>
<p
className="font-semibold"
style={{
background: 'linear-gradient(135deg, #3b82f6, #7c3aed)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{copy.strongRecommend}
</p>
</div>
<div className="flex flex-col gap-3 pt-1">
<button
type="button"
onClick={onSmartSplit}
className="flex w-full items-center justify-center gap-2 rounded-xl py-3.5 text-base font-semibold text-white transition-all hover:opacity-90 active:scale-[0.98]"
style={{ background: 'linear-gradient(135deg, #3b82f6, #7c3aed)' }}
>
<AppIcon name="sparkles" className="h-5 w-5" />
<span>{copy.smartSplitLabel}</span>
<span className="rounded-full bg-white/20 px-2 py-0.5 text-xs">
{copy.smartSplitBadge}
</span>
</button>
<button
type="button"
onClick={onContinue}
className="w-full py-2.5 text-sm text-[var(--glass-text-tertiary)] transition-colors hover:text-[var(--glass-text-secondary)]"
>
{copy.continueLabel}
<span className="ml-1 text-xs opacity-60">
- {copy.continueHint}
</span>
</button>
</div>
</div>
</div>
</div>,
document.body,
)
}

View File

@@ -109,7 +109,7 @@ export default function StoryInputComposer({
return (
<div className="relative w-full glass-surface-elevated rounded-2xl">
<div className="p-6 pb-0">
<div className="p-6 pb-4">
{topRight && (
<div className="mb-3 flex items-center justify-end">
{topRight}