样式修改
This commit is contained in:
1 parent
d9e3a87911
commit
d12c50ea4e
5 files changed
+85
-12
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="62" viewBox="0 0 1920 62"><defs><style>.a{fill:url(#a);}.b{fill:rgba(0,98,217,0.1);}.c{fill:#fff;}.d{fill:rgba(0,98,217,0.2);}.e{filter:url(#h);}.f{filter:url(#e);}.g{filter:url(#b);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#0b3776"/><stop offset="1" stop-color="#0c1935"/></linearGradient><filter id="b"><feOffset dy="-3" input="SourceAlpha"/><feGaussianBlur result="c"/><feFlood flood-color="#0062d9" flood-opacity="0.102" result="d"/><feComposite operator="out" in="SourceGraphic" in2="c"/><feComposite operator="in" in="d"/><feComposite operator="in" in2="SourceGraphic"/></filter><filter id="e"><feOffset dy="-3" input="SourceAlpha"/><feGaussianBlur result="f"/><feFlood flood-color="#0062d9" flood-opacity="0.102" result="g"/><feComposite operator="out" in="SourceGraphic" in2="f"/><feComposite operator="in" in="g"/><feComposite operator="in" in2="SourceGraphic"/></filter><filter id="h"><feOffset dy="-3" input="SourceAlpha"/><feGaussianBlur result="i"/><feFlood flood-color="#0062d9" flood-opacity="0.2" result="j"/><feComposite operator="out" in="SourceGraphic" in2="i"/><feComposite operator="in" in="j"/><feComposite operator="in" in2="SourceGraphic"/></filter></defs><g transform="translate(-2009 -3361)"><rect class="a" width="1920" height="62" transform="translate(2009 3361)"/><g data-type="innerShadowGroup"><path class="b" d="M0,0H318L268,62H0Z" transform="translate(2009 3361)"/><g class="g" transform="matrix(1, 0, 0, 1, 2009, 3361)"><path class="c" d="M0,0H318L268,62H0Z"/></g></g><g data-type="innerShadowGroup"><path class="b" d="M0,0H310L260,62H0Z" transform="translate(2009 3361)"/><g class="f" transform="matrix(1, 0, 0, 1, 2009, 3361)"><path class="c" d="M0,0H310L260,62H0Z"/></g></g><g data-type="innerShadowGroup"><path class="d" d="M0,0H302L252,62H0Z" transform="translate(2009 3361)"/><g class="e" transform="matrix(1, 0, 0, 1, 2009, 3361)"><path class="c" d="M0,0H302L252,62H0Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -9,7 +9,7 @@
|
||||
*
|
||||
* 进入即自动开跑(`useStep3Report.start`),右上角「重新绘制」可重跑。
|
||||
*/
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState, type FormEvent } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import {
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
RefreshCw,
|
||||
SendHorizontal,
|
||||
Sparkles,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
@@ -197,7 +198,7 @@ export function Step3ReportMode({
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={redraw}
|
||||
onClick={() => redraw()}
|
||||
disabled={phase === "creating" || phase === "streaming"}
|
||||
className="bg-white border border-slate-200 text-slate-700 hover:bg-slate-50 disabled:opacity-50 disabled:cursor-not-allowed text-[10.5px] font-bold px-3 py-1.5 rounded-xl flex items-center gap-1 transition"
|
||||
>
|
||||
@@ -311,6 +312,7 @@ export function Step3ReportMode({
|
||||
dialogues={dialogues}
|
||||
error={error}
|
||||
onRetry={redraw}
|
||||
onRevisionSubmit={redraw}
|
||||
onArtifactStepClick={openArtifactFromStep}
|
||||
onArtifactPreview={openArtifactPreview}
|
||||
selectedModel={selectedModel}
|
||||
@@ -432,6 +434,7 @@ function AgentProgressPanel({
|
||||
dialogues,
|
||||
error,
|
||||
onRetry,
|
||||
onRevisionSubmit,
|
||||
onArtifactStepClick,
|
||||
onArtifactPreview,
|
||||
selectedModel,
|
||||
@@ -443,6 +446,7 @@ function AgentProgressPanel({
|
||||
dialogues: ReturnType<typeof useStep3Report>["dialogues"];
|
||||
error: string | null;
|
||||
onRetry: () => void;
|
||||
onRevisionSubmit: (revisionInstruction: string) => void;
|
||||
onArtifactStepClick: ReturnType<typeof useStep3Report>["openArtifactFromStep"];
|
||||
onArtifactPreview: ReturnType<typeof useStep3Report>["openArtifactPreview"];
|
||||
selectedModel: string;
|
||||
@@ -453,6 +457,15 @@ function AgentProgressPanel({
|
||||
const busy = phase === "creating" || phase === "streaming";
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const { atBottom, scrollToBottom } = useAutoScroll(scrollRef, [dialogues]);
|
||||
const [revisionText, setRevisionText] = useState("");
|
||||
|
||||
const handleRevisionSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const text = revisionText.trim();
|
||||
if (!text || busy) return;
|
||||
setRevisionText("");
|
||||
onRevisionSubmit(text);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col bg-slate-50/20">
|
||||
@@ -549,6 +562,39 @@ function AgentProgressPanel({
|
||||
)}
|
||||
</div>
|
||||
{!atBottom && <ScrollToBottomButton onClick={scrollToBottom} />}
|
||||
<form
|
||||
onSubmit={handleRevisionSubmit}
|
||||
className="shrink-0 border-t border-slate-100 bg-white px-3 py-3"
|
||||
>
|
||||
<div className="flex items-end gap-2 rounded-2xl border border-slate-200 bg-slate-50/70 px-3 py-2 focus-within:border-primary/60 focus-within:bg-white focus-within:ring-2 focus-within:ring-[var(--brand-200)]/40">
|
||||
<textarea
|
||||
value={revisionText}
|
||||
onChange={(e) => setRevisionText(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
e.currentTarget.form?.requestSubmit();
|
||||
}
|
||||
}}
|
||||
disabled={busy}
|
||||
rows={1}
|
||||
placeholder="输入修改要求,例如:把右侧页面改成深色风格,并增加风险矩阵"
|
||||
className="min-h-8 max-h-28 flex-1 resize-none bg-transparent py-1.5 text-xs leading-relaxed text-slate-800 placeholder:text-slate-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={busy || revisionText.trim().length === 0}
|
||||
title={busy ? "正在绘制,请稍后发送" : "发送修改要求"}
|
||||
className="mb-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-xl bg-primary text-white transition hover:bg-[var(--brand-900)] disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{busy ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<SendHorizontal className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -72,11 +72,11 @@ export interface UseStep3ReportReturn {
|
||||
/** 当前沙箱预览绑定的 thread id(这里恒为报告线程)。 */
|
||||
activeArtifactThreadId: string | null;
|
||||
/** 启动绘制(幂等:进行中重复调用会被忽略)。 */
|
||||
start: () => void;
|
||||
start: (revisionInstruction?: string) => void;
|
||||
/** 从草稿快照恢复展示(建合成 stub + 气泡 + 打开沙箱,phase=done,不重新绘制)。 */
|
||||
hydrateSaved: (snapshot: DraftStep3Snapshot) => void;
|
||||
/** 重新绘制(先中断在途请求再重跑)。 */
|
||||
redraw: () => void;
|
||||
redraw: (revisionInstruction?: string) => void;
|
||||
/** 中断在途请求(卸载 / 切回普通视图时调用)。 */
|
||||
abort: () => void;
|
||||
/** 点击气泡内工具步骤时打开沙箱(与第 2 步 openArtifactFromStep 同义)。 */
|
||||
@@ -92,8 +92,10 @@ function buildReportPrompt(opts: {
|
||||
intentReady: ReportIntent | null;
|
||||
seatDeliveries: ReportSeatDelivery[];
|
||||
consensusText: string;
|
||||
revisionInstruction?: string;
|
||||
previousHtml?: string;
|
||||
}): string {
|
||||
const { intentReady, seatDeliveries, consensusText } = opts;
|
||||
const { intentReady, seatDeliveries, consensusText, revisionInstruction, previousHtml } = opts;
|
||||
const parts: string[] = [];
|
||||
parts.push(
|
||||
"请基于以下圆桌多智能体研讨的成果,撰写一份图文并茂、以文字叙述为主线的 HTML「方案研讨汇报」," +
|
||||
@@ -135,6 +137,23 @@ function buildReportPrompt(opts: {
|
||||
parts.push("");
|
||||
}
|
||||
|
||||
const cleanRevisionInstruction = revisionInstruction?.trim();
|
||||
if (cleanRevisionInstruction) {
|
||||
parts.push("# User revision request");
|
||||
parts.push(cleanRevisionInstruction);
|
||||
parts.push("");
|
||||
if (previousHtml?.trim()) {
|
||||
parts.push("# Previous rendered HTML to revise");
|
||||
parts.push(
|
||||
"Use this as the baseline. Preserve parts not contradicted by the revision request, and output a complete updated self-contained HTML file.",
|
||||
);
|
||||
parts.push("```html");
|
||||
parts.push(previousHtml.trim());
|
||||
parts.push("```");
|
||||
parts.push("");
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(
|
||||
"请严格遵循你的职责设定:自包含离线(纯 inline SVG 图表、禁止任何外链 CDN)、研讨叙事型结构" +
|
||||
"(结论+KPI → 背景目标 → 各席位观点 → 综合分析 → 最终共识方案 → 执行路线)、" +
|
||||
@@ -173,7 +192,7 @@ function reportPhaseLabel(phase: StreamPhase, toolName?: string): string {
|
||||
export function useStep3Report(
|
||||
options: UseStep3ReportOptions,
|
||||
): UseStep3ReportReturn {
|
||||
const { intentReady, seatDeliveries, consensusText, selectedModel, onReportSaved } =
|
||||
const { intentReady, seatDeliveries, consensusText, selectedModel, savedReport, onReportSaved } =
|
||||
options;
|
||||
|
||||
// onReportSaved 身份可能每次 render 变 —— 打到 ref,避免进 run() 的 deps 反复重建。
|
||||
@@ -601,7 +620,7 @@ export function useStep3Report(
|
||||
runningRef.current = false;
|
||||
}, []);
|
||||
|
||||
const run = useCallback(async () => {
|
||||
const run = useCallback(async (revisionInstruction?: string) => {
|
||||
if (runningRef.current) return;
|
||||
runningRef.current = true;
|
||||
setError(null);
|
||||
@@ -632,7 +651,13 @@ export function useStep3Report(
|
||||
);
|
||||
setPhase("streaming");
|
||||
|
||||
const prompt = buildReportPrompt({ intentReady, seatDeliveries, consensusText });
|
||||
const prompt = buildReportPrompt({
|
||||
intentReady,
|
||||
seatDeliveries,
|
||||
consensusText,
|
||||
revisionInstruction,
|
||||
previousHtml: savedReport?.html,
|
||||
});
|
||||
const res = await streamMultiAgent({
|
||||
agentType: "special",
|
||||
agentName,
|
||||
@@ -687,6 +712,7 @@ export function useStep3Report(
|
||||
intentReady,
|
||||
seatDeliveries,
|
||||
consensusText,
|
||||
savedReport?.html,
|
||||
selectedModel,
|
||||
appendStreamingDialogue,
|
||||
appendToDialogue,
|
||||
@@ -699,8 +725,8 @@ export function useStep3Report(
|
||||
reconcileStubFromRealFile,
|
||||
]);
|
||||
|
||||
const start = useCallback(() => {
|
||||
void run();
|
||||
const start = useCallback((revisionInstruction?: string) => {
|
||||
void run(revisionInstruction);
|
||||
}, [run]);
|
||||
|
||||
// 从草稿快照恢复:不跑 SSE,用保存的 HTML 重建一条「完成」气泡 + 合成 stub,
|
||||
@@ -766,9 +792,9 @@ export function useStep3Report(
|
||||
[abort, buildWriteFileVirtualUrl, openArtifact],
|
||||
);
|
||||
|
||||
const redraw = useCallback(() => {
|
||||
const redraw = useCallback((revisionInstruction?: string) => {
|
||||
abort();
|
||||
void run();
|
||||
void run(revisionInstruction);
|
||||
}, [abort, run]);
|
||||
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user