153 lines
4.6 KiB
TypeScript
153 lines
4.6 KiB
TypeScript
import { ArrowLeft, Loader2, Send } from 'lucide-react';
|
|
import { useMemo, useState } from 'react';
|
|
|
|
import type {
|
|
JumpHopSessionResponse,
|
|
JumpHopWorkspaceCreateRequest,
|
|
} from '../../../../packages/shared/src/contracts/jumpHop';
|
|
import { jumpHopClient } from '../../../services/jump-hop/jumpHopClient';
|
|
|
|
type JumpHopCreationWorkspaceProps = {
|
|
isBusy?: boolean;
|
|
error?: string | null;
|
|
onBack: () => void;
|
|
onSubmitted: (
|
|
result: JumpHopSessionResponse,
|
|
payload: JumpHopWorkspaceCreateRequest,
|
|
) => void;
|
|
showBackButton?: boolean;
|
|
unifiedChrome?: boolean;
|
|
};
|
|
|
|
type JumpHopWorkspaceFormState = {
|
|
themeText: string;
|
|
};
|
|
|
|
const DEFAULT_FORM_STATE: JumpHopWorkspaceFormState = {
|
|
themeText: '',
|
|
};
|
|
|
|
function buildJumpHopWorkspacePayload(
|
|
formState: JumpHopWorkspaceFormState,
|
|
): JumpHopWorkspaceCreateRequest {
|
|
const themeText = formState.themeText.trim();
|
|
return {
|
|
templateId: 'jump-hop',
|
|
themeText,
|
|
workTitle: `${themeText}跳一跳`,
|
|
workDescription: `${themeText}主题的俯视角跳跃作品`,
|
|
themeTags: [themeText, '跳一跳', '休闲'],
|
|
difficulty: 'standard',
|
|
stylePreset: 'minimal-blocks',
|
|
characterPrompt: '内置默认 3D 角色',
|
|
tilePrompt: `${themeText}主题的3D立方体主题身份方块包装图集`,
|
|
endMoodPrompt: null,
|
|
};
|
|
}
|
|
|
|
export function JumpHopCreationWorkspace({
|
|
isBusy = false,
|
|
error = null,
|
|
onBack,
|
|
onSubmitted,
|
|
showBackButton = true,
|
|
unifiedChrome = false,
|
|
}: JumpHopCreationWorkspaceProps) {
|
|
const [formState, setFormState] = useState(DEFAULT_FORM_STATE);
|
|
const [localError, setLocalError] = useState<string | null>(null);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
const canSubmit = useMemo(
|
|
() => Boolean(formState.themeText.trim()),
|
|
[formState],
|
|
);
|
|
|
|
const handleSubmit = async () => {
|
|
if (!canSubmit || isSubmitting || isBusy) {
|
|
setLocalError('请先补全输入。');
|
|
return;
|
|
}
|
|
|
|
setIsSubmitting(true);
|
|
setLocalError(null);
|
|
|
|
try {
|
|
const payload = buildJumpHopWorkspacePayload(formState);
|
|
const response = await jumpHopClient.createSession(payload);
|
|
onSubmitted(response, payload);
|
|
} catch (caughtError) {
|
|
setLocalError(
|
|
caughtError instanceof Error ? caughtError.message : '创建草稿失败。',
|
|
);
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={
|
|
unifiedChrome
|
|
? 'jump-hop-workspace mx-auto flex min-h-0 w-full max-w-none flex-col overflow-visible'
|
|
: 'jump-hop-workspace platform-remap-surface mx-auto flex h-full min-h-0 w-full max-w-3xl flex-col px-3 pb-3 pt-3 sm:px-4 sm:pt-4'
|
|
}
|
|
data-unified-chrome={unifiedChrome ? 'true' : 'false'}
|
|
>
|
|
{showBackButton ? (
|
|
<div className="mb-3 flex items-center justify-between gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={onBack}
|
|
className="platform-button platform-button--ghost min-h-0 px-3 py-2 text-sm"
|
|
>
|
|
<ArrowLeft className="h-4 w-4" />
|
|
返回
|
|
</button>
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="grid gap-3">
|
|
<label className="block sm:col-span-2">
|
|
<span className="text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]">
|
|
主题
|
|
</span>
|
|
<input
|
|
value={formState.themeText}
|
|
onChange={(event) =>
|
|
setFormState((current) => ({
|
|
...current,
|
|
themeText: event.target.value,
|
|
}))
|
|
}
|
|
className="mt-2 w-full rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/90 px-3 py-3 text-sm font-semibold text-[var(--platform-text-strong)] outline-none"
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
{localError || error ? (
|
|
<div className="platform-banner platform-banner--danger mt-3 rounded-2xl text-sm leading-6">
|
|
{localError ?? error}
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="mt-auto flex justify-end gap-2 pb-[max(0.25rem,env(safe-area-inset-bottom))] pt-3">
|
|
<button
|
|
type="button"
|
|
onClick={handleSubmit}
|
|
disabled={!canSubmit || isSubmitting || isBusy}
|
|
className={`platform-button platform-button--primary min-h-11 justify-center gap-2 px-5 py-3 ${!canSubmit || isSubmitting || isBusy ? 'cursor-not-allowed opacity-55' : ''}`}
|
|
>
|
|
{isSubmitting ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
<Send className="h-4 w-4" />
|
|
)}
|
|
生成
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default JumpHopCreationWorkspace;
|