diff --git a/.codex/skills/deerflow-ui-guard/SKILL.md b/.codex/skills/deerflow-ui-guard/SKILL.md new file mode 100644 index 00000000..1ece2023 --- /dev/null +++ b/.codex/skills/deerflow-ui-guard/SKILL.md @@ -0,0 +1,32 @@ +--- +name: deerflow-ui-guard +description: Improve or review UI in the DeerFlow Vite/React frontend while preserving its light, dark-blue, and light-blue themes, Radix/shadcn-style primitives, Chinese enterprise density, embedded routes, and Chrome 109 compatibility. Use for visual refinements, new frontend pages/components, UI reviews, token cleanup, or design-system work under frontend-web/. +--- + +# DeerFlow UI Guard + +Use this skill only in this repository. Preserve the product's existing visual system; do not replace it with a generic SaaS template or a new component library. + +## Read the relevant sources first + +1. Read `references/project-sources.md`, then the relevant theme specification. +2. Inspect the page and the nearest existing components before proposing or changing UI. +3. Read `frontend-web/src/styles/globals.css` and the affected primitives under `frontend-web/src/components/ui/` before changing tokens, shared styles, or variants. + +## Design and implementation rules + +- Reuse the existing Radix/shadcn-style primitives, `lucide-react`, Tailwind v4, and established CSS variables. Do not add Ant Design, another component system, or a visual-effects package unless the user explicitly asks. +- Keep all three themes correct: light/red, dark/blue, and light-blue. Prefer semantic classes and existing variables over raw color values. When a new token is necessary, define matching values for every theme. +- Preserve Chinese enterprise-product density and task completion speed. Make hierarchy, primary action, status, empty states, error recovery, and keyboard focus clearer before adding decoration. +- Keep the shell, deep links, authentication, embed/isolation behavior, sidebar menu semantics, and responsive layout unchanged unless the request explicitly targets them. +- Avoid unscoped global overrides and `!important`; place page-specific styling beside the feature and make selectors local. +- Support the Chrome 109 constraint. Do not introduce unguarded `oklch()`, `oklab()`, `lab()`, `lch()`, or `color-mix()` values. +- Preserve accessible names, keyboard interaction, visible `:focus-visible` states, sufficient contrast, and reduced-motion behavior for new or changed controls. + +## Delivery workflow + +1. State which existing theme and UI primitives the work will reuse. +2. Make the smallest coherent change; do not perform an unrelated visual sweep. +3. Validate with `pnpm typecheck`; when CSS or built UI changes, also run `pnpm build` and `pnpm check:chrome109-css` from `frontend-web/` when practical. +4. If a browser is available, inspect screenshots in light, dark, and light-blue modes. Otherwise say that visual verification remains outstanding. +5. For a review-only request, use `web-design-guidelines` as an additional quality gate when it is available, then distinguish confirmed issues from visual checks that still need a browser. diff --git a/.codex/skills/deerflow-ui-guard/agents/openai.yaml b/.codex/skills/deerflow-ui-guard/agents/openai.yaml new file mode 100644 index 00000000..aeaea8db --- /dev/null +++ b/.codex/skills/deerflow-ui-guard/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "DeerFlow UI Guard" + short_description: "Apply DeerFlow themes and UI safety checks." + default_prompt: "Use $deerflow-ui-guard to improve this DeerFlow UI without breaking its themes or legacy-browser support." diff --git a/.codex/skills/deerflow-ui-guard/references/project-sources.md b/.codex/skills/deerflow-ui-guard/references/project-sources.md new file mode 100644 index 00000000..d23a9d42 --- /dev/null +++ b/.codex/skills/deerflow-ui-guard/references/project-sources.md @@ -0,0 +1,23 @@ +# DeerFlow UI sources + +Read only the files relevant to the requested surface. + +## Canonical themes + +- Light/red: `frontend-web/docs/浅红色UI规范/README.md` and its focused section files. +- Dark/blue: `frontend-web/docs/ui规范-深蓝/README.md` and its focused section files. +- Light-blue: `frontend-web/docs/ui规范-浅蓝/浅蓝色ui规范.md`. +- Runtime tokens and browser fallbacks: `frontend-web/src/styles/globals.css`. + +## Shared implementation + +- UI primitives: `frontend-web/src/components/ui/`. +- Theme selection: `frontend-web/src/contexts/ThemeContext.tsx` and `frontend-web/src/App.tsx`. +- Application shell and embed behavior: `frontend-web/src/pages/PageRoutes.tsx`. +- Dynamic navigation: `frontend-web/src/core/page-layout/use-sidebar-menu.ts`. + +## Required constraints + +- Use `pnpm` in `frontend-web/`. +- Retain Chrome 109 compatibility; validate CSS with `pnpm check:chrome109-css` after a build. +- Do not move or replace public assets, route behavior, or authentication as part of a visual-only change. diff --git a/.gitignore b/.gitignore index aee22e47..5971e86e 100644 --- a/.gitignore +++ b/.gitignore @@ -77,8 +77,14 @@ offline-backend-20260512/backend/.deer-flow/* # Per-user skill_evolution overrides — runtime data written by the API offline-backend-20260512/backend/skill_evolution.json -skills/ -hermes-agent-main/ +skills/ +# Keep the project-local Codex UI guard under version control. The broad +# skills/ rule above still excludes runtime skill directories elsewhere. +!.codex/ +!.codex/skills/ +!.codex/skills/deerflow-ui-guard/ +!.codex/skills/deerflow-ui-guard/** +hermes-agent-main/ .gitignore offline-backend-20260512/backend/tests/* deploy @@ -112,4 +118,4 @@ offline-backend-20260512/backend/uv.lock __pycache__/ # misc -.DS_Store \ No newline at end of file +.DS_Store diff --git a/design-qa.md b/design-qa.md new file mode 100644 index 00000000..fd848f5f --- /dev/null +++ b/design-qa.md @@ -0,0 +1,48 @@ +# Design QA — skills card list + +## Scope + +Only the skill-card list presentation was changed. The page header, summary cards, filters, top actions, sidebar, and existing interactions were left intact. + +## Visual evidence + +- Source visual truth: `C:\Users\admin\AppData\Local\Temp\codex-clipboard-8ca03817-ebd4-43e1-9d7c-2119e87d331c.png` +- Implementation URL: `http://localhost:5173/#/page/workspace/skills` +- Implementation capture: `F:\react01\deeflow-code\deerflow-server\skills-card-list-implementation.png` +- Focused source/implementation comparison: `F:\react01\deeflow-code\deerflow-server\skills-card-list-comparison.png` +- Viewport and state: Edge, desktop light theme, default list mode. The supplied reference contains three sample skills; the live implementation currently exposes one skill, so the comparison focuses on the first card row rather than fabricated data. + +## Findings + +No actionable P0, P1, or P2 visual issue remains within the requested list-card scope. + +- **Hierarchy and layout:** the card now begins with a large semantic skill icon, then groups name, existing status badges, description, and publisher before the existing tag, use-count, state, activity, and action areas. The five-column grid keeps the important skill identity visible while preserving the original live fields. +- **Typography and spacing:** the title, description, metadata, and tags use the existing product type scale with a compact 12px card radius and consistent horizontal rhythm. The row remains responsive and collapses cleanly on narrower widths. +- **Color and surfaces:** category icons use restrained solid blue/green/violet/amber surfaces, tags remain subdued, and enabled status uses a quiet green pill. Borders and hover surfaces follow the existing neutral card treatment; no gradients were introduced. +- **Image and icon fidelity:** the source uses symbolic skill artwork. The implementation uses the closest existing Lucide workflow, globe, file, and upload icons in colored circular surfaces rather than adding screenshot-derived artwork or a new custom asset. +- **Feature fidelity:** all pre-existing action controls, view modes, and list metadata stay visible. The reference's toggle was not copied as a fake interactive control because this page had no enable/disable mutation; its existing `enabled` data is shown as a read-only status pill instead. + +## Comparison history + +1. **[P1, fixed] The original live skill row had little identity or information hierarchy.** + - Earlier state: title, description, metadata, and the action cluster were visually detached, with no category icon or clear scanning order. + - Fix: rebuilt only the list/card row layout around existing data and handlers, added semantic colored icon surfaces, compact state/use/activity columns, and a unified action cluster. + - Evidence: `skills-card-list-comparison.png` contains the reference card row and the live implementation card row in one focused comparison. + +## Interaction and engineering checks + +- Edge interaction check: passed. The live row retains 7 existing action buttons; opening the details action displays its dialog. +- View-mode behavior: passed. The existing list/card view control still switches to card mode (`aria-pressed=true`); the card view uses the same restyled visual language. +- Browser runtime: two guest-bootstrap resource requests returned `401 Unauthorized` after reload. They are unrelated to the skill-card styling and did not prevent the list, details dialog, or view-mode control from working. +- `pnpm build`: passed. The project still emits the pre-existing `/images/deer.svg` runtime-resolution warning. +- `git diff --check`: passed; only existing line-ending notices were emitted for unrelated files. + +## Implementation checklist + +- [x] Skill-card list restyled without changing page chrome or filtering controls +- [x] Existing skill actions, metadata, and view modes retained +- [x] Live `enabled` data displayed without inventing a new mutation control +- [x] Source and implementation reviewed in a single focused comparison image +- [x] Edge interaction and production build verified + +final result: passed diff --git a/frontend-web/public/agent-avatar-source-v2.png b/frontend-web/public/agent-avatar-source-v2.png new file mode 100644 index 00000000..81951098 Binary files /dev/null and b/frontend-web/public/agent-avatar-source-v2.png differ diff --git a/frontend-web/public/agent-avatar-source-v3.png b/frontend-web/public/agent-avatar-source-v3.png new file mode 100644 index 00000000..4195fcbf Binary files /dev/null and b/frontend-web/public/agent-avatar-source-v3.png differ diff --git a/frontend-web/public/agent-avatar-source-v4.png b/frontend-web/public/agent-avatar-source-v4.png new file mode 100644 index 00000000..9be3b898 Binary files /dev/null and b/frontend-web/public/agent-avatar-source-v4.png differ diff --git a/frontend-web/public/agent-avatar-source.png b/frontend-web/public/agent-avatar-source.png new file mode 100644 index 00000000..81ce5924 Binary files /dev/null and b/frontend-web/public/agent-avatar-source.png differ diff --git a/frontend-web/public/agent-avatar-v2-transparent.png b/frontend-web/public/agent-avatar-v2-transparent.png new file mode 100644 index 00000000..81345b8d Binary files /dev/null and b/frontend-web/public/agent-avatar-v2-transparent.png differ diff --git a/frontend-web/public/agent-avatar-v3-transparent.png b/frontend-web/public/agent-avatar-v3-transparent.png new file mode 100644 index 00000000..76035fce Binary files /dev/null and b/frontend-web/public/agent-avatar-v3-transparent.png differ diff --git a/frontend-web/public/agent-avatar-v4-transparent.png b/frontend-web/public/agent-avatar-v4-transparent.png new file mode 100644 index 00000000..d2cd1a3c Binary files /dev/null and b/frontend-web/public/agent-avatar-v4-transparent.png differ diff --git a/frontend-web/public/cm-assistant-logo-source.png b/frontend-web/public/cm-assistant-logo-source.png new file mode 100644 index 00000000..a62deb20 Binary files /dev/null and b/frontend-web/public/cm-assistant-logo-source.png differ diff --git a/frontend-web/src/roundtable-planning/components/Step2Panel.tsx b/frontend-web/src/roundtable-planning/components/Step2Panel.tsx index 891050df..3138127a 100644 --- a/frontend-web/src/roundtable-planning/components/Step2Panel.tsx +++ b/frontend-web/src/roundtable-planning/components/Step2Panel.tsx @@ -20,32 +20,25 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Cpu, FileIcon, - HelpCircle, Loader2, Network, - Paperclip, Pause, Play, - Sparkles, X, } from "lucide-react"; -/** 人类可读的文件大小(B / KB / MB)。 */ -function formatFileSize(bytes: number): string { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - return `${(bytes / 1024 / 1024).toFixed(1)} MB`; -} - /** 后台挂起入口 —— 后端 _run_dag 已支持业务链分层并行,放出该入口。 */ const SHOW_BACKGROUND_SUSPEND = true; -import { AutoGrowTextarea } from "@/components/ui/auto-grow-textarea"; import { useLocalSettings } from "@/core/settings"; import { cn } from "@/lib/utils"; import type { MessageMetric } from "@/components/workspace/messages/use-message-metrics"; import { DialogueSpeedMeter } from "@/roundtable-planning/components/DialogueSpeedMeter"; +import { + Step2ClarificationCard, + Step2InterventionBar, +} from "@/roundtable-planning/components/planning/Step2Composer"; import { PersonaDrawer } from "@/roundtable-planning/components/PersonaDrawer"; import { ScrollToBottomButton } from "@/roundtable-planning/components/ScrollToBottomButton"; import { Step2SandboxLayout } from "@/roundtable-planning/components/Step2SandboxLayout"; @@ -93,75 +86,80 @@ interface ModelOption { } export interface Step2PanelProps { - // ─── State ──────────────────────────────────────────────────── - step2RoundtableDialogues: Step2Dialogue[]; - isInitializing: boolean; - isRoundTableRunning: boolean; - isOrchestrating: boolean; - pendingClarification: ClarificationPayload | null; - clarificationDraft: string; - customAgentInput: string; - hasConsensus: boolean; - agentConfigs: Record; - selectedAgentId: number | null; - roles: RoleEntry[]; - // ─── 按席位筛选会商记录(状态在页面层,与左侧角色卡片共用)────────────── - /** 当前筛选标识;null = 显示全部。 */ - seatFilter: SeatFilter | null; - setSeatFilter: React.Dispatch>; - // ─── 并行显示(§5)──────────────────────────────────────────── - parallelBatch: ParallelSeatView[]; - selectedParallelSeats: string[]; - isParallelRunning: boolean; - onToggleParallelSeat: (agentId: string) => void; - // ─── Phase 7 DAG 流程图执行态总览(§7)───────────────────────── - /** 当前编排模式;=== "dag" 且 plan 非空时,顶部显示 DAG 流程图缩略。 */ - orchestrationMode: "recommend" | "chain" | "dag"; - /** dag 模式的分层计划(流程图按 stage 分列渲染)。 */ - orchestrationPlan: OrchestrationPlan | null; - /** agentId(小写) → 节点状态;驱动流程图节点 active/done。 */ - dagNodeStates: Record; - /** agent id(小写)→ 席位元数据,用于把派活 chip 的 id 显示成中文席位名。 */ - agentNameToRole: Record< - string, - { speakerId: number; avatarType: string; displayName: string } - >; - selectedModel: string; - availableModels: ModelOption[]; - modelsLoading: boolean; - setSelectedModel: React.Dispatch>; - activeArtifactThreadId: string | null; - seatStubMessages: Record; - openArtifactFromStep: ( - dialogue: Step2Dialogue, - step: NonNullable[number], - ) => void; - /** 主聊天里 `present_files` 会在气泡下方渲染卡片;Step 2 沿用同一交互。 */ - openArtifactPreview: (threadId: string, path: string) => void; - // ─── Setters ────────────────────────────────────────────────── - setClarificationDraft: React.Dispatch>; - setCustomAgentInput: React.Dispatch>; - setAgentConfigs: React.Dispatch< - React.SetStateAction> - >; - setSelectedAgentId: React.Dispatch>; - // ─── Handlers ───────────────────────────────────────────────── - onPauseRoundtable: () => void; - onResumeRoundtable: () => void; - onHandleInjectToRoundtable: (files?: File[]) => void; - onSubmitClarification: (answer: string) => void; - onSavePersona: (id: number) => void; - onProceedToStep3: () => void; - /** 「后台挂起」:把当前研讨转入后端自动跑到结果绘制完成。 */ - onBackgroundSuspend: () => void; - /** 是否在「查看历史记录」(从草稿/后台作业加载)—— 为 true 时隐藏「后台挂起」按钮。 */ - isViewingHistory?: boolean; - onTriggerToast: (msg: string) => void; + session: { + dialogues: Step2Dialogue[]; + isInitializing: boolean; + isRoundTableRunning: boolean; + isOrchestrating: boolean; + pendingClarification: ClarificationPayload | null; + clarificationDraft: string; + customAgentInput: string; + hasConsensus: boolean; + agentConfigs: Record; + selectedAgentId: number | null; + roles: RoleEntry[]; + }; + execution: { + seatFilter: SeatFilter | null; + setSeatFilter: React.Dispatch>; + parallelBatch: ParallelSeatView[]; + selectedParallelSeats: string[]; + isParallelRunning: boolean; + onToggleParallelSeat: (agentId: string) => void; + orchestrationMode: "recommend" | "chain" | "dag"; + orchestrationPlan: OrchestrationPlan | null; + dagNodeStates: Record; + agentNameToRole: Record< + string, + { speakerId: number; avatarType: string; displayName: string } + >; + }; + model: { + selectedModel: string; + availableModels: ModelOption[]; + modelsLoading: boolean; + setSelectedModel: React.Dispatch>; + }; + artifacts: { + activeArtifactThreadId: string | null; + seatStubMessages: Record; + openArtifactFromStep: ( + dialogue: Step2Dialogue, + step: NonNullable[number], + ) => void; + openArtifactPreview: (threadId: string, path: string) => void; + }; + setters: { + setClarificationDraft: React.Dispatch>; + setCustomAgentInput: React.Dispatch>; + setAgentConfigs: React.Dispatch< + React.SetStateAction> + >; + setSelectedAgentId: React.Dispatch>; + }; + actions: { + onPauseRoundtable: () => void; + onResumeRoundtable: () => void; + onHandleInjectToRoundtable: (files?: File[]) => void; + onSubmitClarification: (answer: string) => void; + onSavePersona: (id: number) => void; + onProceedToStep3: () => void; + onBackgroundSuspend: () => void; + isViewingHistory?: boolean; + onTriggerToast: (message: string) => void; + }; } -export function Step2Panel(props: Step2PanelProps) { +export function Step2Panel({ + session, + execution, + model, + artifacts, + setters, + actions, +}: Step2PanelProps) { const { - step2RoundtableDialogues, + dialogues: step2RoundtableDialogues, isInitializing, isRoundTableRunning, isOrchestrating, @@ -172,6 +170,8 @@ export function Step2Panel(props: Step2PanelProps) { agentConfigs, selectedAgentId, roles, + } = session; + const { seatFilter, setSeatFilter, parallelBatch, @@ -182,18 +182,26 @@ export function Step2Panel(props: Step2PanelProps) { orchestrationPlan, dagNodeStates, agentNameToRole, + } = execution; + const { selectedModel, availableModels, modelsLoading, setSelectedModel, + } = model; + const { activeArtifactThreadId, seatStubMessages, openArtifactFromStep, openArtifactPreview, + } = artifacts; + const { setClarificationDraft, setCustomAgentInput, setAgentConfigs, setSelectedAgentId, + } = setters; + const { onPauseRoundtable, onResumeRoundtable, onHandleInjectToRoundtable, @@ -203,7 +211,7 @@ export function Step2Panel(props: Step2PanelProps) { onBackgroundSuspend, isViewingHistory, onTriggerToast, - } = props; + } = actions; const step2ScrollRef = useRef(null); const { atBottom: step2AtBottom, scrollToBottom: scrollStep2ToBottom } = @@ -375,34 +383,11 @@ export function Step2Panel(props: Step2PanelProps) { // 干预研审:记录指令并自动恢复会商,右上角按钮状态由 isRoundTableRunning 驱动 const [pauseFlash, setPauseFlash] = useState(false); - // 干预消息携带的本地附件(点「干预并发送」时连同指令一起上传)。 - const [injectFiles, setInjectFiles] = useState([]); - const injectFileInputRef = useRef(null); - - const handlePickInjectFiles = useCallback( - (e: React.ChangeEvent) => { - const picked = Array.from(e.target.files ?? []); - if (picked.length > 0) { - setInjectFiles((prev) => { - const seen = new Set(prev.map((f) => `${f.name}:${f.size}`)); - return [...prev, ...picked.filter((f) => !seen.has(`${f.name}:${f.size}`))]; - }); - } - e.target.value = ""; - }, - [], - ); - - const removeInjectFile = useCallback((idx: number) => { - setInjectFiles((prev) => prev.filter((_, i) => i !== idx)); - }, []); - - const handleInject = useCallback(() => { - onHandleInjectToRoundtable(injectFiles.length > 0 ? injectFiles : undefined); - setInjectFiles([]); + const handleInject = useCallback((files?: File[]) => { + onHandleInjectToRoundtable(files); setPauseFlash(true); window.setTimeout(() => setPauseFlash(false), 1600); - }, [onHandleInjectToRoundtable, injectFiles]); + }, [onHandleInjectToRoundtable]); const isSandboxStreaming = isOrchestrating || @@ -656,166 +641,20 @@ export function Step2Panel(props: Step2PanelProps) { {!step2AtBottom && } - {/* Clarification card */} - {pendingClarification && ( -
-
- -
-
- 总控协调需要澄清 - {pendingClarification.clarificationType && ( - - {pendingClarification.clarificationType} - - )} -
- {pendingClarification.context && ( -

{pendingClarification.context}

- )} -

- {pendingClarification.question} -

- {Array.isArray(pendingClarification.options) && - pendingClarification.options.length > 0 && ( -
- {pendingClarification.options.map((opt, idx) => { - const label = - typeof opt === "string" - ? opt - : opt && typeof opt === "object" && "label" in (opt as object) - ? String((opt as { label: unknown }).label ?? "") - : String(opt ?? ""); - if (!label) return null; - return ( - - ); - })} -
- )} - {pendingClarification.allowCustom !== false && ( -
- setClarificationDraft(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - onSubmitClarification(clarificationDraft); - } - }} - placeholder="输入你的回答…(Enter 发送 / Shift+Enter 换行)" - className="flex-1 bg-white border border-amber-300 rounded-xl px-3 py-2 text-xs leading-relaxed focus:outline-none focus:border-amber-500 focus:ring-1 focus:ring-amber-200 font-medium" - maxHeight={280} - /> - -
- )} -
-
-
- )} + - {/* Console + intervention bar */} -
- {/* 已选附件预览(点「干预并发送」时随指令上传) */} - {injectFiles.length > 0 && ( -
- {injectFiles.map((file, idx) => ( - - - - {file.name} - - - {formatFileSize(file.size)} - - - - ))} -
- )} -
- setCustomAgentInput(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - handleInject(); - } - }} - placeholder="作为指挥官,您可以随时插话输入指令,对智能体博弈朝向实施人工干预...(Enter 发送 / Shift+Enter 换行)" - className="flex-1 bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs leading-relaxed focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 font-medium" - maxHeight={280} - /> - - - - - - - -
-
+ ); diff --git a/frontend-web/src/roundtable-planning/components/Step3FileBrowseMode.tsx b/frontend-web/src/roundtable-planning/components/Step3FileBrowseMode.tsx index 3d211866..bf4c5c24 100644 --- a/frontend-web/src/roundtable-planning/components/Step3FileBrowseMode.tsx +++ b/frontend-web/src/roundtable-planning/components/Step3FileBrowseMode.tsx @@ -2,6 +2,7 @@ * Step 3「文件沙箱预览」模式 —— 布局对齐「结果绘制」:左栏研讨内容 + 文件清单,右栏 Step 2 沙箱。 */ import { useCallback, useEffect, useMemo, useState } from "react"; +import { useDefaultLayout } from "react-resizable-panels"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import { @@ -75,6 +76,10 @@ export function Step3FileBrowseMode({ }: Step3FileBrowseModeProps) { const [activeFile, setActiveFile] = useState(initialFile); const [leftCollapsed, setLeftCollapsed] = useState(false); + const { defaultLayout, onLayoutChanged } = useDefaultLayout({ + id: "roundtable-step3-file-browse-layout", + panelIds: ["content", "artifact"], + }); useEffect(() => { openArtifactPreview(activeFile.threadId, activeFile.file.path); @@ -163,9 +168,10 @@ export function Step3FileBrowseMode({ - +