From 5cd02aa8b093bdfbfdeb0e0f65f970b6cbf44ef4 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Wed, 16 Sep 2026 03:59:27 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=99=BA=E8=83=BD=E4=BD=93?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E5=B1=82=E7=BA=A7=E5=B9=B6=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=E5=AF=BC=E5=87=BA=E4=B8=8E=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当前Agent与策划Agent共用正文和过程表现组件,统一思考及工具调用的小字号浅色样式 素材工具栏按实际动作分组,导出紧邻删除并修复空分组重复分隔线 文档与代码使用共享Markdown预览和代码高亮,复用按需读取与缓存 移除未调用的活跃回合查询命令,保留正式快照恢复链路 补充回归用例并同步技术方案和团队约定 --- apps/ai-game-creator-shell/package.json | 1 + .../src-tauri/src/agent/direct_runtime/mod.rs | 14 +- .../src-tauri/src/main.rs | 1 - apps/ai-game-creator-shell/src/app/types.ts | 13 -- .../ChatMarkdownMessage/codeHighlight.css | 37 +++++ .../components/ChatMarkdownMessage/index.tsx | 74 +++++++-- .../ProjectSupervisorView.tsx | 143 ++++++++++------- .../project-workspace/ToolCallGroup.tsx | 7 +- .../resource-canvas/resourceCanvasChrome.css | 35 ++++- .../resourceCanvasFocusModel.ts | 4 +- .../resourceDocumentPreviewModel.ts | 80 ++++++++++ apps/ai-game-creator-shell/src/styles.css | 28 +--- .../ResourceDocumentPreviewDialog.tsx | 87 +++++++++++ .../src/view/project-development/index.tsx | 141 ++++++++++++----- .../resourceCardPreviewModel.ts | 5 +- .../useProjectResourceCardPreviews.ts | 25 +-- .../tests/AgentMessageContent.test.tsx | 111 ++++++++++++++ .../tests/ChatMarkdownMessage.test.tsx | 40 +++++ .../ResourceDocumentPreviewDialog.test.tsx | 126 +++++++++++++++ .../tests/appSurface/design-agent.suite.ts | 6 + .../tests/appSurface/tool-call-group.suite.ts | 1 + .../resourceCanvasFloatingDismiss.test.tsx | 14 ++ .../tests/resourceCanvasToolbarModel.test.ts | 28 ++++ .../resourceDocumentPreviewModel.test.ts | 74 +++++++++ .../useProjectResourceCardPreviews.test.ts | 59 +++++++ ...施计划】文档素材Markdown预览-2026-09-16.md | 17 +++ ...里程碑】文档素材Markdown预览-2026-09-16.md | 23 +++ .../shared-memory/team-conventions.md | 1 + ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 13 ++ ...方案】GameAgent对话工具调用卡片-2026-09-14.md | 2 +- package-lock.json | 141 +++++++++++++++++ .../src/components/AgentMessageContent.css | 25 +++ .../src/components/AgentMessageContent.tsx | 24 +++ packages/shared/src/components/index.ts | 2 + ...ageCanvasSelectedLayerToolbarView.test.tsx | 120 ++++++++++++++- .../ImageCanvasSelectedLayerToolbarView.tsx | 144 ++++++++++++------ src/index.css | 12 -- 37 files changed, 1439 insertions(+), 239 deletions(-) create mode 100644 apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/codeHighlight.css create mode 100644 apps/ai-game-creator-shell/src/features/resource-canvas/resourceDocumentPreviewModel.ts create mode 100644 apps/ai-game-creator-shell/src/view/project-development/ResourceDocumentPreviewDialog.tsx create mode 100644 apps/ai-game-creator-shell/tests/AgentMessageContent.test.tsx create mode 100644 apps/ai-game-creator-shell/tests/ResourceDocumentPreviewDialog.test.tsx create mode 100644 apps/ai-game-creator-shell/tests/resourceDocumentPreviewModel.test.ts create mode 100644 docs/project-memory/plans/【实施计划】文档素材Markdown预览-2026-09-16.md create mode 100644 docs/project-memory/plans/【里程碑】文档素材Markdown预览-2026-09-16.md create mode 100644 packages/shared/src/components/AgentMessageContent.css create mode 100644 packages/shared/src/components/AgentMessageContent.tsx diff --git a/apps/ai-game-creator-shell/package.json b/apps/ai-game-creator-shell/package.json index c08cb6ac9..650ebf029 100644 --- a/apps/ai-game-creator-shell/package.json +++ b/apps/ai-game-creator-shell/package.json @@ -57,6 +57,7 @@ "react-colorful": "^5.8.0", "react-dom": "^19.0.0", "react-markdown": "^10.1.0", + "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.1", "vite": "^6.2.0", "zustand": "^5.0.14" diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs index 08c7f56be..a515954cb 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs @@ -444,7 +444,8 @@ pub(crate) fn direct_taonier_active_invocation_id_at(root: &Path) -> Result Result, String> { @@ -475,16 +477,6 @@ pub(crate) fn read_direct_taonier_active_invocation_at( })) } -/// 只读命令:读取该项目当前登记的 Direct 活跃回合,供前端重进会话时恢复。 -/// -/// 不进入、不抢占、不释放守卫;没有回合时返回 `null`。 -#[tauri::command] -pub(crate) fn read_direct_codex_active_turn( - project_path: String, -) -> Result, String> { - read_direct_taonier_active_invocation_at(Path::new(project_path.trim())) -} - /// "终止"拿不到可中断句柄时的分类,决定是否允许强制释放本地守卫。 #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum DirectTaonierStaleGuardReason { diff --git a/apps/ai-game-creator-shell/src-tauri/src/main.rs b/apps/ai-game-creator-shell/src-tauri/src/main.rs index 9a6c48a3b..bec55a981 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/main.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/main.rs @@ -2677,7 +2677,6 @@ fn main() { chat_with_game_creator_role_agent, chat_with_game_creator_role_agent_stream, chat_with_game_creator_direct_codex, - read_direct_codex_active_turn, cancel_direct_codex_turn, select_game_creator_reasoning_effort, start_planning_session_v2, diff --git a/apps/ai-game-creator-shell/src/app/types.ts b/apps/ai-game-creator-shell/src/app/types.ts index b71988918..1a8e48d72 100644 --- a/apps/ai-game-creator-shell/src/app/types.ts +++ b/apps/ai-game-creator-shell/src/app/types.ts @@ -1204,19 +1204,6 @@ interface TurnStreamItemBase { */ export type TurnStreamItem = TurnStreamTextItem | TurnStreamToolItem; -/** - * `read_direct_codex_active_turn` 的返回值:Rust 进程内当前登记的 Direct 活跃回合。 - * - * 重进会话时用它把前端的"当前活跃回合"接管回来,否则界面不知道有回合在跑, - * 既不显示过程卡也不给终止入口,用户再发消息只会被守卫拒绝。 - */ -export interface DirectActiveTurnView { - /** 与回合事件的 `turnId` 同一个身份。 */ - clientTurnId: string; - /** 这一轮登记的时刻(Unix 毫秒)。 */ - startedAt: number; -} - /** `cancel_direct_codex_turn` 的返回值。 */ export interface DirectTurnCancelView { /** diff --git a/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/codeHighlight.css b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/codeHighlight.css new file mode 100644 index 000000000..d212361a5 --- /dev/null +++ b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/codeHighlight.css @@ -0,0 +1,37 @@ +/* 只作用于共享 Markdown 渲染器,不改变普通正文及用户消息的字体颜色。 */ +.agc-markdown-code .hljs-comment, +.agc-markdown-code .hljs-quote { + color: #6a737d; +} + +.agc-markdown-code .hljs-keyword, +.agc-markdown-code .hljs-name, +.agc-markdown-code .hljs-selector-tag, +.agc-markdown-code .hljs-literal, +.agc-markdown-code .hljs-deletion { + color: #a6264c; +} + +.agc-markdown-code .hljs-string, +.agc-markdown-code .hljs-regexp, +.agc-markdown-code .hljs-addition { + color: #276438; +} + +.agc-markdown-code .hljs-number, +.agc-markdown-code .hljs-attr, +.agc-markdown-code .hljs-variable, +.agc-markdown-code .hljs-built_in { + color: #075a9c; +} + +.agc-markdown-code .hljs-title, +.agc-markdown-code .hljs-type, +.agc-markdown-code .hljs-section { + color: #6f42a0; +} + +.agc-markdown-code .hljs-meta, +.agc-markdown-code .hljs-symbol { + color: #8a4c0a; +} diff --git a/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx index d743dc2c2..d8acce153 100644 --- a/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx +++ b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx @@ -1,3 +1,5 @@ +import './codeHighlight.css'; + import type { ErrorInfo, ReactNode } from 'react'; import { Children, @@ -7,14 +9,20 @@ import { useContext, } from 'react'; import ReactMarkdown, { type Components } from 'react-markdown'; +import rehypeHighlight from 'rehype-highlight'; import remarkGfm from 'remark-gfm'; export type ChatMarkdownMessageProps = { text: string; role: 'assistant' | 'user'; streaming?: boolean; + /** 文件预览不压缩正文空行,保留源码与文档的原始排版。 */ + preserveBlankLines?: boolean; }; +const MAX_HIGHLIGHT_CHARACTERS = 100_000; +const CodeBlockContext = createContext(false); + /** 只压缩普通 Markdown 正文里多余的空行;代码块中的换行必须原样保留。 */ function normalizeMarkdownBlankLines(text: string) { return text @@ -183,22 +191,50 @@ const markdownComponents: Components = { a: ({ children }) => children, img: ({ alt }) => (alt?.trim() ? `图片:${alt}` : '图片已省略'), h1: ({ children }) => ( -

{children}

+

+ {children} +

), h2: ({ children }) => ( -

{children}

+

+ {children} +

), h3: ({ children }) => ( -

{children}

+

+ {children} +

), h4: ({ children }) => ( -

{children}

+

+ {children} +

), h5: ({ children }) => ( -
{children}
+
+ {children} +
), h6: ({ children }) => ( -
+
{children}
), @@ -213,15 +249,18 @@ const markdownComponents: Components = { ), pre: ({ children }) => (
-      {children}
+      
+        {children}
+      
     
), - code: ({ className, children, node: _node, ...props }) => { - const isBlock = - Boolean(className?.includes('language-')) || - String(children).includes('\n'); + code: function MarkdownCode({ className, children, node: _node, ...props }) { + const isBlock = useContext(CodeBlockContext); return isBlock ? ( - + {children} ) : ( @@ -235,7 +274,10 @@ const markdownComponents: Components = { }, table: ({ children }) => (
- +
{children}
@@ -264,6 +306,7 @@ export function ChatMarkdownMessage({ text, role, streaming = false, + preserveBlankLines = false, }: ChatMarkdownMessageProps) { if (role === 'user') { return {text}; @@ -274,11 +317,14 @@ export function ChatMarkdownMessage({ - {normalizeMarkdownBlankLines(text)} + {preserveBlankLines ? text : normalizeMarkdownBlankLines(text)} ); diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index b2a9a0728..4124f3a5a 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -8,6 +8,10 @@ import type { } from 'react'; import { Fragment, useEffect, useRef, useState } from 'react'; +import { + AgentMessageContent, + type AgentMessageTone, +} from '../../../../../packages/shared/src/components/AgentMessageContent'; import type { AgentStatusCard, ChatMessage, @@ -131,6 +135,7 @@ function TurnStreamSequence({ active, userSentAt, className, + tone = 'body', }: { items: readonly TurnStreamItem[]; toolCalls: readonly GameCreatorDirectToolCall[]; @@ -138,6 +143,7 @@ function TurnStreamSequence({ active: boolean; userSentAt: number; className?: string; + tone?: AgentMessageTone; }) { const runs = turnStreamRuns(items); const callsById = new Map(); @@ -159,11 +165,13 @@ function TurnStreamSequence({ : 'message message--assistant' } > - + + + ) : ( + 思考过程 +
{text}
+ + ); +} + type RuntimePanelProps = ComponentProps; function directStatusTitle(status: string | null | undefined) { @@ -437,7 +469,11 @@ export function ProjectSupervisorView({

); }; - const renderMessage = (message: ChatMessage, index: number) => { + const renderMessageContent = ( + message: ChatMessage, + index: number, + tone: AgentMessageTone, + ) => { const sentAt = message.role === 'user' ? directMessageTimestamp(message.updatedAt) : 0; return ( @@ -445,10 +481,12 @@ export function ProjectSupervisorView({ key={message.messageId ?? `${message.role}-${index}`} className={`message message--${message.role}`} > - + + + {sentAt > 0 ? (