1
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
import { type ReactNode,useMemo, useState } from 'react';
|
||||
import { type ReactNode, useMemo, useState } from 'react';
|
||||
|
||||
import { normalizeCustomWorldLandmarks } from '../data/customWorldSceneGraph';
|
||||
import { Character, CustomWorldProfile } from '../types';
|
||||
import { getNineSliceStyle, UI_CHROME } from '../uiAssets';
|
||||
import { CustomWorldEntityCatalog, type ResultTab } from './CustomWorldEntityCatalog';
|
||||
import { type CustomWorldEditorTarget,CustomWorldEntityEditorModal } from './CustomWorldEntityEditorModal';
|
||||
import {
|
||||
CustomWorldEntityCatalog,
|
||||
type ResultTab,
|
||||
} from './CustomWorldEntityCatalog';
|
||||
import {
|
||||
type CustomWorldEditorTarget,
|
||||
CustomWorldEntityEditorModal,
|
||||
} from './CustomWorldEntityEditorModal';
|
||||
|
||||
interface CustomWorldResultViewProps {
|
||||
profile: CustomWorldProfile;
|
||||
@@ -17,8 +23,13 @@ interface CustomWorldResultViewProps {
|
||||
onEditSetting?: () => void;
|
||||
onRegenerate?: () => void;
|
||||
onContinueExpand?: () => void;
|
||||
onSave: () => void;
|
||||
onSave?: () => void;
|
||||
onProfileChange: (profile: CustomWorldProfile) => void;
|
||||
readOnly?: boolean;
|
||||
backLabel?: string;
|
||||
editActionLabel?: string;
|
||||
regenerateActionLabel?: string;
|
||||
saveActionLabel?: string;
|
||||
}
|
||||
|
||||
function SmallButton({
|
||||
@@ -48,7 +59,9 @@ function SmallButton({
|
||||
);
|
||||
}
|
||||
|
||||
function getCreateTargetByTab(activeTab: ResultTab): CustomWorldEditorTarget | null {
|
||||
function getCreateTargetByTab(
|
||||
activeTab: ResultTab,
|
||||
): CustomWorldEditorTarget | null {
|
||||
if (activeTab === 'playable') return { kind: 'playable', mode: 'create' };
|
||||
if (activeTab === 'story') return { kind: 'story', mode: 'create' };
|
||||
if (activeTab === 'landmarks') return { kind: 'landmark', mode: 'create' };
|
||||
@@ -82,7 +95,10 @@ function removeStoryNpcsFromProfile(
|
||||
} satisfies CustomWorldProfile;
|
||||
}
|
||||
|
||||
function removeLandmarksFromProfile(profile: CustomWorldProfile, ids: string[]) {
|
||||
function removeLandmarksFromProfile(
|
||||
profile: CustomWorldProfile,
|
||||
ids: string[],
|
||||
) {
|
||||
const idSet = new Set(ids);
|
||||
const nextLandmarks = profile.landmarks.filter(
|
||||
(landmark) => !idSet.has(landmark.id),
|
||||
@@ -115,12 +131,24 @@ export function CustomWorldResultView({
|
||||
onContinueExpand,
|
||||
onSave,
|
||||
onProfileChange,
|
||||
readOnly = false,
|
||||
backLabel = '返回',
|
||||
editActionLabel = '修改设定',
|
||||
regenerateActionLabel = '重新生成',
|
||||
saveActionLabel = '保存到我的作品',
|
||||
}: CustomWorldResultViewProps) {
|
||||
const [editorTarget, setEditorTarget] = useState<CustomWorldEditorTarget | null>(null);
|
||||
const [editorTarget, setEditorTarget] =
|
||||
useState<CustomWorldEditorTarget | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<ResultTab>('world');
|
||||
|
||||
const createTarget = useMemo(() => getCreateTargetByTab(activeTab), [activeTab]);
|
||||
const createLabel = useMemo(() => getCreateLabelByTab(activeTab), [activeTab]);
|
||||
const createTarget = useMemo(
|
||||
() => getCreateTargetByTab(activeTab),
|
||||
[activeTab],
|
||||
);
|
||||
const createLabel = useMemo(
|
||||
() => getCreateLabelByTab(activeTab),
|
||||
[activeTab],
|
||||
);
|
||||
const onRegenerate = () => {
|
||||
if (isGenerating || !triggerRegenerate) return;
|
||||
|
||||
@@ -151,7 +179,7 @@ export function CustomWorldResultView({
|
||||
disabled={isGenerating}
|
||||
className={`self-start rounded-full border border-white/10 bg-black/18 px-3 py-1.5 text-[11px] text-zinc-300 transition-colors hover:text-white ${isGenerating ? 'opacity-45' : ''}`}
|
||||
>
|
||||
返回
|
||||
{backLabel}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -165,15 +193,22 @@ export function CustomWorldResultView({
|
||||
onProfileChange={onProfileChange}
|
||||
onDeleteStoryNpcs={handleDeleteStoryNpcs}
|
||||
onDeleteLandmarks={handleDeleteLandmarks}
|
||||
createActionLabel={createLabel}
|
||||
onCreateAction={createTarget ? () => setEditorTarget(createTarget) : undefined}
|
||||
createActionLabel={readOnly ? undefined : createLabel}
|
||||
onCreateAction={
|
||||
readOnly || !createTarget
|
||||
? undefined
|
||||
: () => setEditorTarget(createTarget)
|
||||
}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isGenerating && (
|
||||
<div className="mt-3 rounded-2xl border border-sky-400/18 bg-sky-500/10 px-4 py-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="text-sm font-semibold text-white">{progressLabel}</div>
|
||||
<div className="text-sm font-semibold text-white">
|
||||
{progressLabel}
|
||||
</div>
|
||||
<div className="text-xs text-sky-100">{Math.round(progress)}%</div>
|
||||
</div>
|
||||
<div className="mt-3 h-3 overflow-hidden rounded-full border border-white/10 bg-black/35">
|
||||
@@ -199,28 +234,41 @@ export function CustomWorldResultView({
|
||||
) : null}
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
{onEditSetting ? (
|
||||
<SmallButton onClick={onEditSetting}>修改设定</SmallButton>
|
||||
<SmallButton onClick={onEditSetting}>{editActionLabel}</SmallButton>
|
||||
) : null}
|
||||
{triggerRegenerate ? (
|
||||
<SmallButton onClick={onRegenerate} tone="sky">重新生成</SmallButton>
|
||||
<SmallButton onClick={onRegenerate} tone="sky">
|
||||
{regenerateActionLabel}
|
||||
</SmallButton>
|
||||
) : null}
|
||||
{profile.generationStatus === 'key_only' && onContinueExpand ? (
|
||||
<SmallButton onClick={onContinueExpand} tone="sky" disabled={isGenerating}>
|
||||
<SmallButton
|
||||
onClick={onContinueExpand}
|
||||
tone="sky"
|
||||
disabled={isGenerating}
|
||||
>
|
||||
继续补全世界
|
||||
</SmallButton>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSave}
|
||||
disabled={isGenerating}
|
||||
className={`pixel-nine-slice pixel-pressable text-left ${isGenerating ? 'opacity-55' : ''}`}
|
||||
style={getNineSliceStyle(UI_CHROME.choiceButton, { paddingX: 16, paddingY: 10 })}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="text-sm font-semibold text-white">保存到我的作品</span>
|
||||
<span className="text-white/60">→</span>
|
||||
</div>
|
||||
</button>
|
||||
{onSave ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSave}
|
||||
disabled={isGenerating}
|
||||
className={`pixel-nine-slice pixel-pressable text-left ${isGenerating ? 'opacity-55' : ''}`}
|
||||
style={getNineSliceStyle(UI_CHROME.choiceButton, {
|
||||
paddingX: 16,
|
||||
paddingY: 10,
|
||||
})}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="text-sm font-semibold text-white">
|
||||
{saveActionLabel}
|
||||
</span>
|
||||
<span className="text-white/60">→</span>
|
||||
</div>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user