调整创作主页顶部与左侧栏
更新 /creation 顶部栏和左侧功能栏的参考图规格 接入创作主页 image2 陶土风背景、图标和按钮素材 保留创作主页现有文案、搜索、登录、钱包、导航与项目创建逻辑 补充创作主页改版文档和移动端直达验证
This commit is contained in:
@@ -1,16 +1,3 @@
|
||||
import {
|
||||
Box,
|
||||
Code2,
|
||||
Gamepad2,
|
||||
Image as ImageIcon,
|
||||
Layers3,
|
||||
Megaphone,
|
||||
Music,
|
||||
Palette,
|
||||
Plus,
|
||||
Sparkles,
|
||||
Wand2,
|
||||
} from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
@@ -56,7 +43,7 @@ type ShowcaseTabId =
|
||||
type CreationFeatureItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
icon: typeof Palette;
|
||||
iconSrc: string;
|
||||
};
|
||||
|
||||
type ShowcaseAssetItem = {
|
||||
@@ -70,51 +57,57 @@ type ShowcaseAssetItem = {
|
||||
cost: string;
|
||||
};
|
||||
|
||||
const CREATION_HOME_ASSETS = {
|
||||
heroCreate: '/creation-home/hero-create.png',
|
||||
heroCommunity: '/creation-home/hero-community.png',
|
||||
projectNew: '/creation-home/project-new.png',
|
||||
} as const;
|
||||
|
||||
const CREATION_FEATURES: CreationFeatureItem[] = [
|
||||
{
|
||||
title: '游戏视觉规范',
|
||||
description: '轻松约束多类素材视觉一致性',
|
||||
icon: Palette,
|
||||
iconSrc: '/creation-home/feature-visual-spec.png',
|
||||
},
|
||||
{
|
||||
title: '游戏角色',
|
||||
description: '整套高完成度2D/3D角色素材及动画',
|
||||
icon: Box,
|
||||
iconSrc: '/creation-home/feature-character.png',
|
||||
},
|
||||
{
|
||||
title: '游戏UI',
|
||||
description: '轻松生成高一致性的UI素材',
|
||||
icon: Layers3,
|
||||
iconSrc: '/creation-home/feature-ui.png',
|
||||
},
|
||||
{
|
||||
title: '游戏音乐',
|
||||
description: '轻松制作游戏背景音乐、音效',
|
||||
icon: Music,
|
||||
iconSrc: '/creation-home/feature-music.png',
|
||||
},
|
||||
{
|
||||
title: '游戏特效',
|
||||
description: '低成本生成精品游戏特效',
|
||||
icon: Sparkles,
|
||||
iconSrc: '/creation-home/feature-effects.png',
|
||||
},
|
||||
{
|
||||
title: '游戏场景',
|
||||
description: '轻松生成各类游戏场景素材',
|
||||
icon: ImageIcon,
|
||||
iconSrc: '/creation-home/feature-scene.png',
|
||||
},
|
||||
{
|
||||
title: '游戏美宣',
|
||||
description: '轻松生成游戏上架所需图片和视频',
|
||||
icon: Megaphone,
|
||||
iconSrc: '/creation-home/feature-marketing.png',
|
||||
},
|
||||
{
|
||||
title: '游戏功能编码',
|
||||
description: '自然语言驱动游戏生成',
|
||||
icon: Code2,
|
||||
iconSrc: '/creation-home/feature-coding.png',
|
||||
},
|
||||
{
|
||||
title: '一键导出',
|
||||
description: '支持导出为微抖小游戏、Godot、Unity、UE工程',
|
||||
icon: Gamepad2,
|
||||
iconSrc: '/creation-home/feature-export.png',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -479,18 +472,35 @@ export function CreationLandingView({
|
||||
<div className="creation-landing__hero-actions">
|
||||
<PlatformActionButton
|
||||
size="md"
|
||||
shape="pill"
|
||||
className="creation-landing__hero-action creation-landing__hero-action--primary"
|
||||
onClick={createProject}
|
||||
disabled={isCreatingProject}
|
||||
>
|
||||
<Wand2 className="h-4 w-4" />
|
||||
<img
|
||||
src={CREATION_HOME_ASSETS.heroCreate}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-landing__button-art"
|
||||
/>
|
||||
开始创作
|
||||
</PlatformActionButton>
|
||||
{onOpenCommunity ? (
|
||||
<PlatformActionButton
|
||||
tone="secondary"
|
||||
size="md"
|
||||
shape="pill"
|
||||
className="creation-landing__hero-action creation-landing__hero-action--secondary"
|
||||
onClick={onOpenCommunity}
|
||||
>
|
||||
<img
|
||||
src={CREATION_HOME_ASSETS.heroCommunity}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-landing__button-art"
|
||||
/>
|
||||
玩家社区
|
||||
</PlatformActionButton>
|
||||
) : null}
|
||||
@@ -510,18 +520,26 @@ export function CreationLandingView({
|
||||
</div>
|
||||
</div>
|
||||
<div className="creation-landing__feature-grid">
|
||||
{CREATION_FEATURES.map((feature) => {
|
||||
const Icon = feature.icon;
|
||||
return (
|
||||
<article key={feature.title} className="creation-landing__feature">
|
||||
{CREATION_FEATURES.map((feature) => (
|
||||
<article key={feature.title} className="creation-landing__feature">
|
||||
<span className="creation-landing__feature-figure">
|
||||
<span className="creation-landing__feature-icon">
|
||||
<Icon className="h-5 w-5" />
|
||||
<img
|
||||
src={feature.iconSrc}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
loading="lazy"
|
||||
draggable={false}
|
||||
className="creation-landing__feature-art"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<span className="creation-landing__feature-copy">
|
||||
<h3>{feature.title}</h3>
|
||||
<p>{feature.description}</p>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -552,7 +570,13 @@ export function CreationLandingView({
|
||||
disabled={isCreatingProject}
|
||||
>
|
||||
<span>
|
||||
<Plus className="h-5 w-5" />
|
||||
<img
|
||||
src={CREATION_HOME_ASSETS.projectNew}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-landing__project-new-art"
|
||||
/>
|
||||
</span>
|
||||
新建项目
|
||||
</button>
|
||||
|
||||
@@ -15268,6 +15268,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
? 'create'
|
||||
: platformBootstrap.platformTab
|
||||
}
|
||||
allowHiddenActiveTab={selectionStage === 'creation-home'}
|
||||
onTabChange={handlePlatformHomeTabChange}
|
||||
hasSavedGame={hasSavedGame}
|
||||
savedSnapshot={savedSnapshot}
|
||||
@@ -15428,7 +15429,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
initial={{ opacity: 0, y: 12 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -12 }}
|
||||
className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden"
|
||||
className="creation-home-stage flex h-full min-h-0 min-w-0 flex-col overflow-hidden"
|
||||
>
|
||||
{platformHomeView}
|
||||
</motion.div>
|
||||
|
||||
@@ -9169,6 +9169,23 @@ test('creation home desktop rail can return to platform tabs', async () => {
|
||||
expect(window.location.pathname).toBe('/');
|
||||
});
|
||||
|
||||
test('direct mobile creation home keeps landing content without mobile nav entry', async () => {
|
||||
window.history.replaceState(null, '', '/creation');
|
||||
const { container } = render(<TestWrapper withAuth />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('main', { name: '陶泥儿创作主页' }),
|
||||
).toBeTruthy();
|
||||
const bottomNav = container.querySelector('.platform-bottom-nav');
|
||||
expect(bottomNav).toBeTruthy();
|
||||
expect(
|
||||
within(bottomNav as HTMLElement).queryByRole('button', { name: '创作' }),
|
||||
).toBeNull();
|
||||
expect(
|
||||
within(bottomNav as HTMLElement).queryByRole('button', { name: '项目' }),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test('embedded puzzle form timeout exits busy state and shows a readable error', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
|
||||
@@ -288,6 +288,7 @@ export interface RpgEntryHomeViewProps {
|
||||
createTabContent?: ReactNode;
|
||||
draftTabContent?: ReactNode;
|
||||
hasUnreadDraftUpdate?: boolean;
|
||||
allowHiddenActiveTab?: boolean;
|
||||
}
|
||||
|
||||
const PANEL_SURFACE_CLASS = 'platform-surface platform-surface--soft';
|
||||
@@ -319,6 +320,19 @@ const PLATFORM_DESKTOP_NAV_TABS: PlatformNavTab[] = [
|
||||
'projects',
|
||||
'profile',
|
||||
];
|
||||
const CREATION_HOME_NAV_ICON_SRC: Record<PlatformNavTab, string> = {
|
||||
home: '/creation-home/nav-recommend.png',
|
||||
category: '/creation-home/nav-discover.png',
|
||||
create: '/creation-home/nav-create.png',
|
||||
projects: '/creation-home/nav-projects.png',
|
||||
saves: '/creation-home/nav-projects.png',
|
||||
profile: '/creation-home/nav-profile.png',
|
||||
};
|
||||
const CREATION_HOME_TOPBAR_ASSETS = {
|
||||
brandMascot: '/creation-home/brand-mascot.png',
|
||||
search: '/creation-home/topbar-search.png',
|
||||
wallet: '/creation-home/topbar-wallet.png',
|
||||
} as const;
|
||||
const AVATAR_MAX_FILE_SIZE = 5 * 1024 * 1024;
|
||||
const AVATAR_OUTPUT_SIZE = 256;
|
||||
const AVATAR_ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']);
|
||||
@@ -1011,6 +1025,13 @@ function PublicCodeSearchBar({
|
||||
<div
|
||||
className={`platform-desktop-search flex min-w-0 items-center gap-3 px-4 py-3 text-[var(--platform-text-soft)] ${className ?? ''}`}
|
||||
>
|
||||
<img
|
||||
src={CREATION_HOME_TOPBAR_ASSETS.search}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-home-topbar-art creation-home-topbar-art--search"
|
||||
/>
|
||||
<Search className="h-4 w-4 shrink-0" />
|
||||
<input
|
||||
value={value}
|
||||
@@ -1065,6 +1086,13 @@ function TopbarWalletShortcutButton({
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<img
|
||||
src={CREATION_HOME_TOPBAR_ASSETS.wallet}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-home-topbar-art creation-home-topbar-art--wallet"
|
||||
/>
|
||||
<PlatformIconBadge
|
||||
icon={<Coins className="h-3.5 w-3.5" />}
|
||||
size="xs"
|
||||
@@ -1868,6 +1896,7 @@ function DesktopTabButton({
|
||||
emphasized = false,
|
||||
showDot = false,
|
||||
disabled = false,
|
||||
creationHomeIconSrc,
|
||||
}: {
|
||||
active: boolean;
|
||||
label: string;
|
||||
@@ -1876,6 +1905,7 @@ function DesktopTabButton({
|
||||
emphasized?: boolean;
|
||||
showDot?: boolean;
|
||||
disabled?: boolean;
|
||||
creationHomeIconSrc?: string;
|
||||
}) {
|
||||
const ariaLabel = showDot ? `${label},有新草稿` : label;
|
||||
|
||||
@@ -1888,6 +1918,15 @@ function DesktopTabButton({
|
||||
className={`platform-desktop-rail__button ${emphasized ? 'platform-desktop-rail__button--primary' : ''} ${active ? 'platform-desktop-rail__button--active' : ''} disabled:cursor-not-allowed disabled:opacity-55`}
|
||||
>
|
||||
<span className="platform-desktop-rail__icon-shell">
|
||||
{creationHomeIconSrc ? (
|
||||
<img
|
||||
src={creationHomeIconSrc}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-home-nav-art"
|
||||
/>
|
||||
) : null}
|
||||
<Icon className="platform-desktop-rail__icon h-[1.1rem] w-[1.1rem]" />
|
||||
{showDot ? (
|
||||
<span aria-hidden="true" className="platform-nav-unread-dot" />
|
||||
@@ -2597,6 +2636,7 @@ export function RpgEntryHomeView({
|
||||
createTabContent,
|
||||
draftTabContent,
|
||||
hasUnreadDraftUpdate = false,
|
||||
allowHiddenActiveTab = false,
|
||||
}: RpgEntryHomeViewProps) {
|
||||
const authUi = useAuthUi();
|
||||
const showRechargeEntry = shouldShowRechargeEntry();
|
||||
@@ -2935,11 +2975,15 @@ export function RpgEntryHomeView({
|
||||
} as const satisfies Record<PlatformNavTab, string>;
|
||||
|
||||
useEffect(() => {
|
||||
if (allowHiddenActiveTab) {
|
||||
return;
|
||||
}
|
||||
if (!visibleMobileTabs.includes(activeTab) && !isDesktopLayout) {
|
||||
onTabChange(isAuthenticated ? 'home' : 'category');
|
||||
}
|
||||
}, [
|
||||
activeTab,
|
||||
allowHiddenActiveTab,
|
||||
isAuthenticated,
|
||||
isDesktopLayout,
|
||||
onTabChange,
|
||||
@@ -5096,8 +5140,17 @@ export function RpgEntryHomeView({
|
||||
<div className="flex h-full min-h-0 flex-col">
|
||||
<div className="platform-desktop-shell flex h-full min-h-0 flex-col p-5 xl:p-6">
|
||||
<div className="platform-desktop-topbar flex items-center gap-4 px-5 py-4">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-5">
|
||||
<RpgEntryBrandLogo className="shrink-0" decorative />
|
||||
<div className="platform-desktop-topbar__brand-search flex min-w-0 flex-1 items-center gap-5">
|
||||
<span className="creation-home-brand-lockup shrink-0">
|
||||
<img
|
||||
src={CREATION_HOME_TOPBAR_ASSETS.brandMascot}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
draggable={false}
|
||||
className="creation-home-brand-lockup__art"
|
||||
/>
|
||||
<RpgEntryBrandLogo className="shrink-0" decorative />
|
||||
</span>
|
||||
<PublicCodeSearchBar
|
||||
value={desktopSearchKeyword}
|
||||
onChange={updateDesktopSearchKeyword}
|
||||
@@ -5109,7 +5162,7 @@ export function RpgEntryHomeView({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="platform-desktop-topbar__actions flex items-center gap-3">
|
||||
{isAuthenticated &&
|
||||
(activeTab === 'create' || activeTab === 'saves') ? (
|
||||
<TopbarWalletShortcutButton
|
||||
@@ -5162,6 +5215,7 @@ export function RpgEntryHomeView({
|
||||
active={isPlatformHomeTab(tab) && activeTab === tab}
|
||||
label={tabLabels[tab]}
|
||||
icon={tabIcons[tab]}
|
||||
creationHomeIconSrc={CREATION_HOME_NAV_ICON_SRC[tab]}
|
||||
emphasized={tab === 'create'}
|
||||
showDot={false}
|
||||
disabled={isRechargePaymentConfirmationPending}
|
||||
|
||||
+581
-83
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user