AGC 导出面板抽出 common 面板组件并让小红书复用

- 把 PaneButton 从 tabs/xiaohongshu 移到 tabs/common,供小红书与陶泥儿共用

- 新增 common/ChatPane,统一第二个会话 chatbox 的挂载与状态投影

- 小红书 chat Pane 改为委托 common/ChatPane,五个卡片组件导入路径同步更新
This commit is contained in:
2026-10-06 17:48:35 +08:00
parent b0c16d985d
commit d4797abb85
8 changed files with 83 additions and 38 deletions
@@ -0,0 +1,64 @@
import type { DirectProjectChatBoxProps } from '../../../chat/components/DirectProjectChatBox/DirectProjectChatBox';
import { DirectProjectChatBox } from '../../../chat/components/DirectProjectChatBox/DirectProjectChatBox';
/**
* 导出工作台右半的 chatbox:原样复用主聊天的对话盒(`DirectProjectChatBox`)。
*
* 外壳用 `.game-workbench-chat`:对话盒那套 `.project-chat-surface.is-direct-codex` 几何
* 是按这个祖先写的,换成自造容器就会串版。会话真相不在这里——订阅、回合与草稿都在面板根的
* tab 状态 hook 上,这里只是把第二个 subscriber 的投影喂给同一个对话盒。
*
* 小红书小工具与陶泥儿发布共用这一份;差异只有 `aria` 文案与忙态兜底文案。两个 tab 的状态
* hook 都提供同一组字段,`ExportChatPaneTab` 就是这份结构合同。
*/
export type ExportChatPaneTab = {
chat: DirectProjectChatBoxProps['controller'];
assets: DirectProjectChatBoxProps['assets'];
versions?: DirectProjectChatBoxProps['versions'];
projectPath: DirectProjectChatBoxProps['projectPath'];
turns: DirectProjectChatBoxProps['turns'];
turnStatus: DirectProjectChatBoxProps['turnStatus'];
activeTurnStartedAt: DirectProjectChatBoxProps['activeTurnStartedAt'];
draft: DirectProjectChatBoxProps['draft'];
runtimeNotice: string;
setDraft: (draft: NonNullable<DirectProjectChatBoxProps['draft']>) => void;
};
export function ExportChatPane({
tab,
ariaLabel,
chatAriaLabel,
fallbackStatusText,
}: {
tab: ExportChatPaneTab;
/** `aside` 的语义标签(这一栏)。 */
ariaLabel: string;
/** 对话盒自己的语义标签。 */
chatAriaLabel: string;
/** 没有运行提示、也没有控制器提示时的兜底文案。 */
fallbackStatusText: string;
}) {
return (
<aside
className="game-workbench-chat h-full min-h-0"
aria-label={ariaLabel}
>
<DirectProjectChatBox
ariaLabel={chatAriaLabel}
controller={tab.chat}
assets={tab.assets}
{...(tab.versions ? { versions: tab.versions } : {})}
projectPath={tab.projectPath}
turns={tab.turns}
turnStatus={tab.turnStatus}
activeTurnStartedAt={tab.activeTurnStartedAt}
statusText={
tab.runtimeNotice || tab.chat.statusNotice || fallbackStatusText
}
referencesEnabled={false}
draft={tab.draft}
onDraftChange={(next) => tab.setDraft(next.content)}
/>
</aside>
);
}
@@ -3,9 +3,10 @@ import type { ReactNode } from 'react';
/**
* 面板里的按钮。语气与尺寸收在这一处,别在每个卡片里各抄一遍 Tailwind 串。
*
* `brand` 是小红书红;`hero` 只给「打包」那颗主行动按钮——整行宽、字号更大,是这一页唯一一个
* 「点下去就产出东西」的动作;其余是次要动作(复制、下载、重试),尺寸跟发布面板
* (`GameDistributionPublishPanel`)对齐:10px/14px 内边距、10px 圆角、600 字重。
* `brand` 是小红书红,`primary` 是陶泥儿橙(发布);`hero` 只给每个工作台那颗主行动按钮——
* 整行宽、字号更大,是一页里唯一一个「点下去就产出东西」的动作;其余是次要动作(复制、下载、
* 重试),尺寸跟发布面板(`GameDistributionPublishPanel`)对齐:10px/14px 内边距、10px 圆角、
* 600 字重。
*/
export function PaneButton({
children,
@@ -1,5 +1,5 @@
import { ThemedModal } from '../../../../../components/modal/ThemedModal';
import { PaneButton } from './PaneButton';
import { PaneButton } from '../common/PaneButton';
/**
* 「还没配置好导出」的阻断提示。
@@ -1,11 +1,11 @@
import type { XhsMinitoolExportState } from '../../state/useXhsMinitoolExport';
import { saveXhsMinitoolFileToDisk } from '../../state/xhsMinitoolDownload';
import { XHS_MINITOOL_ARTIFACT_RELATIVE_PATH } from '../../state/xhsMinitoolInstruction';
import { PaneButton } from '../common/PaneButton';
import { AdaptPromptDialog } from './AdaptPromptDialog';
import { ConflictCard } from './ConflictCard';
import { FailureDialog } from './FailureDialog';
import { FormCard } from './FormCard';
import { PaneButton } from './PaneButton';
/**
* 小红书工作台左半:平台上传表单 + 小工具代码包 + 「打包」。
@@ -3,7 +3,7 @@ import { useState } from 'react';
import type { SaveConflict } from '../../generated/SaveConflict';
import type { XHSMiniToolExportForm } from '../../generated/XHSMiniToolExportForm';
import { XHS_MINITOOL_FORM_FIELDS } from '../../state/xhsMinitoolFields';
import { PaneButton } from './PaneButton';
import { PaneButton } from '../common/PaneButton';
type Side = 'mine' | 'file';
@@ -5,7 +5,7 @@ import {
describeXhsMinitoolFailure,
XhsMinitoolFailureRepairability,
} from '../../state/xhsMinitoolFailure';
import { PaneButton } from './PaneButton';
import { PaneButton } from '../common/PaneButton';
/**
* 失败弹窗:一句用户看得懂的话 + 一颗按「谁有能力修」选出来的按钮。
@@ -7,9 +7,9 @@ import { ICON_IMPORTER_SETTINGS } from '../../../../../components/AssetImporter/
import type { FormInvalid } from '../../generated/FormInvalid';
import type { XHSMiniToolExportForm } from '../../generated/XHSMiniToolExportForm';
import { XHS_MINITOOL_FORM_FIELDS } from '../../state/xhsMinitoolFields';
import { PaneButton } from '../common/PaneButton';
import { CopyButton } from './CopyButton';
import { IconPreview } from './IconPreview';
import { PaneButton } from './PaneButton';
/**
* 小红书上传表单。名称与介绍可复制(它们是要粘贴到上传页的文字),图标只给「下载」——复制一条
@@ -1,39 +1,19 @@
import { DirectProjectChatBox } from '../../../../chat/components/DirectProjectChatBox/DirectProjectChatBox';
import type { XiaohongshuTabState } from '../../../state/useXiaohongshuTab';
import { ExportChatPane } from '../../common/ChatPane';
/**
* 右半 chatbox:**原样复用**主聊天的对话盒(`DirectProjectChatBox`)。
* 小红书工作台右半 chatbox:复用 `common` 的导出对话栏。
*
* 外壳用 `.game-workbench-chat`:对话盒那套 `.project-chat-surface.is-direct-codex` 几何
* 是按这个祖先写的,换成自造容器就会串版。会话真相不在这里——订阅、回合与草稿都在面板根的
* `useXiaohongshuTab` 上,这里只是把第二个 subscriber 的投影喂给同一个对话盒。
*
* 与主聊天的差别只有一处:输入区关掉素材引用(`referencesEnabled={false}`),
* 草稿提升到面板层,Composer 随 tab 页卸载再挂回来也不丢字。
* 会话真相在面板根的 `useXiaohongshuTab`:这里只是把第二个 subscriber 的投影交给共用组件,
* 与主聊天的差别只有输入区关掉素材引用、草稿提升到面板层。
*/
export function ChatPane({ tab }: { tab: XiaohongshuTabState }) {
const { chat, turnStatus } = tab;
return (
<aside
className="game-workbench-chat h-full min-h-0"
aria-label="小红书小工具导出对话栏"
>
<DirectProjectChatBox
ariaLabel="小红书小工具导出对话"
controller={chat}
assets={tab.assets}
{...(tab.versions ? { versions: tab.versions } : {})}
projectPath={tab.projectPath}
turns={tab.turns}
turnStatus={turnStatus}
activeTurnStartedAt={tab.activeTurnStartedAt}
statusText={
tab.runtimeNotice || chat.statusNotice || '小红书小工具导出会话'
}
referencesEnabled={false}
draft={tab.draft}
onDraftChange={(next) => tab.setDraft(next.content)}
/>
</aside>
<ExportChatPane
tab={tab}
ariaLabel="小红书小工具导出对话栏"
chatAriaLabel="小红书小工具导出对话"
fallbackStatusText="小红书小工具导出会话"
/>
);
}