This commit is contained in:
2026-04-22 20:14:15 +08:00
parent 0773a0d0ca
commit 0e9c286a57
205 changed files with 25790 additions and 1623 deletions

View File

@@ -0,0 +1,180 @@
import { ArrowRight, X } from 'lucide-react';
export interface PlatformEntryCreationTypeModalProps {
isOpen: boolean;
isBusy: boolean;
error: string | null;
onClose: () => void;
onSelectRpg: () => void;
onSelectBigFish: () => void;
onSelectPuzzle: () => void;
}
type CreationGameTypeCard = {
id: 'rpg' | 'big-fish' | 'puzzle' | 'airp' | 'visual-novel';
title: string;
subtitle: string;
badge: string;
locked: boolean;
};
const CREATION_GAME_TYPES: CreationGameTypeCard[] = [
{
id: 'rpg',
title: '角色扮演 RPG',
subtitle: 'Agent 共创',
badge: '可创建',
locked: false,
},
{
id: 'big-fish',
title: '大鱼吃小鱼',
subtitle: '实时成长玩法',
badge: '可创建',
locked: false,
},
{
id: 'puzzle',
title: '拼图玩法',
subtitle: '图像锚点共创',
badge: '可创建',
locked: false,
},
{
id: 'airp',
title: 'AIRP',
subtitle: '敬请期待',
badge: '锁定',
locked: true,
},
{
id: 'visual-novel',
title: '视觉小说',
subtitle: '敬请期待',
badge: '锁定',
locked: true,
},
];
function CreationTypeCard(props: {
item: CreationGameTypeCard;
busy: boolean;
onSelect: () => void;
}) {
const { item, busy, onSelect } = props;
const disabled = item.locked || busy;
return (
<button
type="button"
disabled={disabled}
onClick={onSelect}
className={`platform-interactive-card relative overflow-hidden rounded-[1.65rem] border px-4 py-4 text-left ${
item.locked
? 'cursor-not-allowed border-[var(--platform-subpanel-border)] bg-[var(--platform-subpanel-fill)] text-[var(--platform-text-soft)]'
: 'border-[var(--platform-cool-border)] bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.24),transparent_34%),linear-gradient(135deg,rgba(255,79,139,0.96),rgba(255,145,110,0.9))] text-white'
} ${busy && !item.locked ? 'opacity-70' : ''}`}
>
<div className="flex items-start justify-between gap-3">
<span
className={`platform-pill px-3 ${
item.locked
? 'platform-pill--neutral text-[var(--platform-text-soft)]'
: 'platform-pill--neutral border-white/30 bg-white/18 text-white'
}`}
>
{item.locked ? item.badge : busy ? '正在开启' : item.badge}
</span>
{item.locked ? (
<span className="text-lg leading-none text-white/45">·</span>
) : (
<ArrowRight className="h-4 w-4 text-white/80" />
)}
</div>
<div className="mt-8 text-xl font-black leading-tight text-inherit">
{item.title}
</div>
<div
className={`mt-2 text-sm ${
item.locked ? 'text-zinc-500' : 'text-zinc-200/82'
}`}
>
{item.subtitle}
</div>
</button>
);
}
/**
* 平台入口创作类型弹层。
* 多玩法入口统一在这里分流,避免把非 RPG 玩法写进 RPG 命名脚本。
*/
export function PlatformEntryCreationTypeModal({
isOpen,
isBusy,
error,
onClose,
onSelectRpg,
onSelectBigFish,
onSelectPuzzle,
}: PlatformEntryCreationTypeModalProps) {
if (!isOpen) {
return null;
}
return (
<div className="platform-overlay fixed inset-0 z-[90] flex items-end justify-center p-3 backdrop-blur-sm sm:items-center sm:p-4">
<div className="platform-modal-shell w-full max-w-3xl overflow-hidden rounded-[1.8rem]">
<div className="bg-transparent">
<div className="flex items-start justify-between gap-3 border-b border-[var(--platform-subpanel-border)] px-4 py-4 sm:px-5">
<div>
<div className="text-base font-semibold text-[var(--platform-text-strong)]">
</div>
<div className="mt-1 text-xs text-[var(--platform-text-base)]">
</div>
</div>
<button
type="button"
onClick={onClose}
disabled={isBusy}
className="platform-icon-button disabled:cursor-not-allowed disabled:opacity-45"
>
<X className="h-4 w-4" />
</button>
</div>
<div className="px-4 py-4 sm:px-5 sm:py-5">
<div className="grid gap-3 sm:grid-cols-5">
{CREATION_GAME_TYPES.map((item) => (
<CreationTypeCard
key={item.id}
item={item}
busy={isBusy}
onSelect={() => {
if (item.id === 'rpg') {
onSelectRpg();
}
if (item.id === 'big-fish') {
onSelectBigFish();
}
if (item.id === 'puzzle') {
onSelectPuzzle();
}
}}
/>
))}
</div>
{error ? (
<div className="platform-banner platform-banner--danger mt-4 rounded-[1.25rem] text-sm leading-6">
{error}
</div>
) : null}
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,17 @@
import { PlatformEntryFlowShellImpl } from './PlatformEntryFlowShellImpl';
import type {
PlatformEntryFlowShellProps,
SelectionStage,
} from './platformEntryTypes';
export type { PlatformEntryFlowShellProps, SelectionStage };
/**
* 平台入口通用壳层。
* RPG、Big Fish 等玩法创作入口在这里并列分流。
*/
export function PlatformEntryFlowShell(props: PlatformEntryFlowShellProps) {
return <PlatformEntryFlowShellImpl {...props} />;
}
export default PlatformEntryFlowShell;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
/**
* 平台首页视图的通用出口。
* 当前先复用成熟的首页实现,但避免让 `platform-entry` 继续直接依赖 RPG 命名组件。
*/
export {
RpgEntryHomeView as PlatformEntryHomeView,
type RpgEntryHomeViewProps as PlatformEntryHomeViewProps,
type PlatformHomeTab,
} from '../rpg-entry/RpgEntryHomeView';

View File

@@ -0,0 +1,8 @@
/**
* 平台作品详情视图的通用出口。
* 这里保留平台语义封装,减少 Big Fish 继续挂在 RPG 命名路径上的误导。
*/
export {
RpgEntryWorldDetailView as PlatformEntryWorldDetailView,
type RpgEntryWorldDetailViewProps as PlatformEntryWorldDetailViewProps,
} from '../rpg-entry/RpgEntryWorldDetailView';

View File

@@ -0,0 +1,9 @@
export {
PlatformEntryFlowShell,
type PlatformEntryFlowShellProps,
type SelectionStage,
} from './PlatformEntryFlowShell';
export {
PlatformEntryCreationTypeModal,
type PlatformEntryCreationTypeModalProps,
} from './PlatformEntryCreationTypeModal';

View File

@@ -0,0 +1,9 @@
/**
* 平台入口共享 helper 的通用封装层。
* 先复用既有实现,同时把多玩法入口依赖从 RPG 命名中隔离出来。
*/
export {
buildCreationHubFallbackItems,
normalizeAgentBackedProfile,
resolveRpgCreationErrorMessage,
} from '../rpg-entry/rpgEntryShared';

View File

@@ -0,0 +1,41 @@
import type {
CustomWorldAgentSessionSnapshot,
} from '../../../packages/shared/src/contracts/customWorldAgent';
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
import type { CustomWorldProfile, GameState } from '../../types';
export type SelectionStage =
| 'platform'
| 'detail'
| 'agent-workspace'
| 'big-fish-agent-workspace'
| 'big-fish-result'
| 'big-fish-runtime'
| 'puzzle-agent-workspace'
| 'puzzle-result'
| 'puzzle-gallery-detail'
| 'puzzle-runtime'
| 'custom-world-generating'
| 'custom-world-result';
export type CustomWorldGenerationViewSource = 'agent-draft-foundation' | null;
export type CustomWorldResultViewSource = 'saved-profile' | 'agent-draft' | null;
export type CustomWorldAutoSaveState = 'idle' | 'saving' | 'saved' | 'error';
export type SyncedAgentDraftResult = {
session: CustomWorldAgentSessionSnapshot | null;
profile: CustomWorldProfile | null;
};
export type PlatformEntryFlowShellProps = {
selectionStage: SelectionStage;
setSelectionStage: (stage: SelectionStage) => void;
gameState: GameState;
hasSavedGame: boolean;
savedSnapshot: HydratedSavedGameSnapshot | null;
handleContinueGame: (snapshot?: HydratedSavedGameSnapshot | null) => void;
handleStartNewGame: () => void;
handleCustomWorldSelect: (customWorldProfile: CustomWorldProfile) => void;
};

View File

@@ -0,0 +1,5 @@
/**
* 平台入口 bootstrap 通用封装。
* 现阶段逻辑仍复用既有实现,但对外暴露平台语义命名。
*/
export { useRpgEntryBootstrap as usePlatformEntryBootstrap } from '../rpg-entry/useRpgEntryBootstrap';

View File

@@ -0,0 +1,5 @@
/**
* 平台入口详情态编排通用封装。
* 通过平台语义出口避免 Big Fish 直接依赖 RPG 命名 hook。
*/
export { useRpgEntryLibraryDetail as usePlatformEntryLibraryDetail } from '../rpg-entry/useRpgEntryLibraryDetail';

View File

@@ -0,0 +1,5 @@
/**
* 平台入口导航通用封装。
* 多玩法统一从 `platform-entry` 暴露RPG 目录只保留兼容与 RPG 专属能力。
*/
export { useRpgEntryNavigation as usePlatformEntryNavigation } from '../rpg-entry/useRpgEntryNavigation';