继续收口工具弹窗与分段切换预设

新增 PlatformToolModalShell 承接白底工具弹窗壳层和固定可访问名称

新增 PlatformSegmentedTabPresets 沉淀频道下划线、创作 pill rail 与二列 option segment

迁移拼图、抓大鹅、历史素材弹窗和首页 / 作品架 / 充值切换的重复组件写法

同步 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
2026-06-11 16:32:56 +08:00
parent 7c47ad3358
commit ffcffef6d2
15 changed files with 848 additions and 621 deletions

View File

@@ -19,7 +19,6 @@ import {
useMemo,
useState,
} from 'react';
import { createPortal } from 'react-dom';
import type { CreationAudioAsset } from '../../../packages/shared/src/contracts/creationAudio';
import type { Match3DResultDraft } from '../../../packages/shared/src/contracts/match3dAgent';
@@ -51,7 +50,6 @@ import {
type Match3DDecodedSpritesheetRegion,
} from '../../services/match3dSpritesheetParser';
import { readPuzzleReferenceImageAsDataUrl } from '../../services/puzzleReferenceImage';
import { useAuthUi } from '../auth/AuthUiContext';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformBackActionButton } from '../common/PlatformBackActionButton';
import { PlatformAssetPickerGrid } from '../common/PlatformAssetPickerCard';
@@ -59,7 +57,6 @@ import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformIconButton } from '../common/PlatformIconButton';
import { PlatformMediaFrame } from '../common/PlatformMediaFrame';
import { PlatformMediaTileGrid } from '../common/PlatformMediaTileGrid';
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
import { PlatformMudPointConfirmDialog } from '../common/PlatformMudPointConfirmDialog';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import { PlatformPillSwitch } from '../common/PlatformPillSwitch';
@@ -70,6 +67,7 @@ import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
import { PlatformTagEditor } from '../common/PlatformTagEditor';
import { PlatformTextField } from '../common/PlatformTextField';
import { PlatformToolModalShell } from '../common/PlatformToolModalShell';
import { PlatformUploadPreviewCard } from '../common/PlatformUploadPreviewCard';
import { useMudPointConfirmController } from '../common/useMudPointConfirmController';
import {
@@ -1459,43 +1457,18 @@ function Match3DModalShell({
children: ReactNode;
onClose: () => void;
}) {
const platformTheme = useAuthUi()?.platformTheme ?? 'light';
if (typeof document === 'undefined') {
return null;
}
return createPortal(
<div
className={`platform-theme platform-theme--${platformTheme} platform-overlay fixed inset-0 z-[146] flex items-end justify-center p-3 backdrop-blur-sm sm:items-center sm:p-4`}
onClick={(event) => {
if (event.target === event.currentTarget) {
onClose();
}
}}
return (
<PlatformToolModalShell
open
title={title}
onClose={onClose}
closeLabel="关闭"
zIndexClassName="z-[146]"
size="xl"
panelClassName="!max-h-[min(92vh,48rem)]"
>
<div
role="dialog"
aria-modal="true"
aria-label={title}
className="platform-modal-shell platform-remap-surface flex max-h-[min(92vh,48rem)] w-full max-w-5xl flex-col overflow-hidden rounded-t-[1.75rem] shadow-[0_24px_80px_rgba(0,0,0,0.55)] sm:rounded-[1.75rem]"
onClick={(event) => event.stopPropagation()}
>
<div className="flex items-center justify-between gap-3 border-b border-[var(--platform-subpanel-border)] px-5 py-4">
<div className="text-base font-semibold text-[var(--platform-text-strong)]">
{title}
</div>
<PlatformModalCloseButton
onClick={onClose}
label="关闭"
variant="platformIcon"
/>
</div>
<div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">
{children}
</div>
</div>
</div>,
document.body,
{children}
</PlatformToolModalShell>
);
}
@@ -1766,92 +1739,22 @@ function Match3DPublishDialog({
onUploadedImageRemove: () => void;
onSubmitCover: () => void;
}) {
const platformTheme = useAuthUi()?.platformTheme ?? 'light';
const publishReady = blockers.length === 0;
if (typeof document === 'undefined') {
return null;
}
return createPortal(
<div
className={`platform-theme platform-theme--${platformTheme} platform-overlay fixed inset-0 z-[146] flex items-end justify-center p-3 backdrop-blur-sm sm:items-center sm:p-4`}
onClick={(event) => {
if (event.target === event.currentTarget && !isGeneratingCover) {
onClose();
}
}}
>
<div
role="dialog"
aria-modal="true"
aria-label="发布抓大鹅作品"
className="platform-modal-shell platform-remap-surface flex max-h-[min(92vh,50rem)] w-full max-w-5xl flex-col overflow-hidden rounded-t-[1.75rem] shadow-[0_24px_80px_rgba(0,0,0,0.55)] sm:rounded-[1.75rem]"
onClick={(event) => event.stopPropagation()}
>
<div className="flex items-center justify-between gap-3 border-b border-[var(--platform-subpanel-border)] px-5 py-4">
<div className="text-base font-semibold text-[var(--platform-text-strong)]">
</div>
<PlatformModalCloseButton
onClick={onClose}
disabled={isGeneratingCover}
label="关闭"
variant="platformIcon"
className={isGeneratingCover ? 'cursor-not-allowed opacity-55' : ''}
/>
</div>
<div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">
<div className="mb-4 space-y-2">
<PlatformFieldLabel variant="section"></PlatformFieldLabel>
{publishError ? (
<PlatformStatusMessage tone="error" surface="platform">
{publishError}
</PlatformStatusMessage>
) : publishReady ? (
<PlatformStatusMessage tone="success" surface="platform">
</PlatformStatusMessage>
) : (
<div className="grid gap-2 sm:grid-cols-2">
{blockers.map((blocker, index) => (
<PlatformStatusMessage
key={`match3d-publish-blocker-${index}-${blocker}`}
tone="warning"
surface="platform"
>
{blocker}
</PlatformStatusMessage>
))}
</div>
)}
</div>
<PlatformFieldLabel variant="section" className="mb-3 block">
</PlatformFieldLabel>
<Match3DCoverImageEditor
editState={editState}
sourceAssets={sourceAssets}
isGenerating={isGeneratingCover}
uploadedImageSrc={uploadedImageSrc}
referenceImages={referenceImages}
aiRedraw={aiRedraw}
prompt={prompt}
error={coverError}
onAiRedrawChange={onAiRedrawChange}
onFileChange={onFileChange}
onPromptChange={onPromptChange}
onReferenceSelect={onReferenceSelect}
onReferenceFileChange={onReferenceFileChange}
onReferenceRemove={onReferenceRemove}
onUploadedImageRemove={onUploadedImageRemove}
onSubmit={onSubmitCover}
/>
</div>
<div className="flex flex-col-reverse gap-3 border-t border-[var(--platform-subpanel-border)] px-5 py-4 sm:flex-row sm:justify-end">
return (
<PlatformToolModalShell
open
title="发布抓大鹅作品"
onClose={onClose}
closeLabel="关闭"
closeDisabled={isGeneratingCover}
closeOnBackdrop={!isGeneratingCover}
zIndexClassName="z-[146]"
size="xl"
panelClassName="!max-h-[min(92vh,50rem)]"
footerClassName="flex-col-reverse sm:flex-row sm:justify-end"
footer={
<>
<PlatformActionButton
onClick={onClose}
disabled={isGeneratingCover || isPublishing}
@@ -1870,10 +1773,56 @@ function Match3DPublishDialog({
)}
广
</PlatformActionButton>
</div>
</>
}
>
<div className="mb-4 space-y-2">
<PlatformFieldLabel variant="section"></PlatformFieldLabel>
{publishError ? (
<PlatformStatusMessage tone="error" surface="platform">
{publishError}
</PlatformStatusMessage>
) : publishReady ? (
<PlatformStatusMessage tone="success" surface="platform">
</PlatformStatusMessage>
) : (
<div className="grid gap-2 sm:grid-cols-2">
{blockers.map((blocker, index) => (
<PlatformStatusMessage
key={`match3d-publish-blocker-${index}-${blocker}`}
tone="warning"
surface="platform"
>
{blocker}
</PlatformStatusMessage>
))}
</div>
)}
</div>
</div>,
document.body,
<PlatformFieldLabel variant="section" className="mb-3 block">
</PlatformFieldLabel>
<Match3DCoverImageEditor
editState={editState}
sourceAssets={sourceAssets}
isGenerating={isGeneratingCover}
uploadedImageSrc={uploadedImageSrc}
referenceImages={referenceImages}
aiRedraw={aiRedraw}
prompt={prompt}
error={coverError}
onAiRedrawChange={onAiRedrawChange}
onFileChange={onFileChange}
onPromptChange={onPromptChange}
onReferenceSelect={onReferenceSelect}
onReferenceFileChange={onReferenceFileChange}
onReferenceRemove={onReferenceRemove}
onUploadedImageRemove={onUploadedImageRemove}
onSubmit={onSubmitCover}
/>
</PlatformToolModalShell>
);
}