样式修改
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`),右上角「重新绘制」可重跑。
|
* 进入即自动开跑(`useStep3Report.start`),右上角「重新绘制」可重跑。
|
||||||
*/
|
*/
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState, type FormEvent } from "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import {
|
import {
|
||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
PanelLeftClose,
|
PanelLeftClose,
|
||||||
PanelLeftOpen,
|
PanelLeftOpen,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
|
SendHorizontal,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@@ -197,7 +198,7 @@ export function Step3ReportMode({
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={redraw}
|
onClick={() => redraw()}
|
||||||
disabled={phase === "creating" || phase === "streaming"}
|
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"
|
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}
|
dialogues={dialogues}
|
||||||
error={error}
|
error={error}
|
||||||
onRetry={redraw}
|
onRetry={redraw}
|
||||||
|
onRevisionSubmit={redraw}
|
||||||
onArtifactStepClick={openArtifactFromStep}
|
onArtifactStepClick={openArtifactFromStep}
|
||||||
onArtifactPreview={openArtifactPreview}
|
onArtifactPreview={openArtifactPreview}
|
||||||
selectedModel={selectedModel}
|
selectedModel={selectedModel}
|
||||||
@@ -432,6 +434,7 @@ function AgentProgressPanel({
|
|||||||
dialogues,
|
dialogues,
|
||||||
error,
|
error,
|
||||||
onRetry,
|
onRetry,
|
||||||
|
onRevisionSubmit,
|
||||||
onArtifactStepClick,
|
onArtifactStepClick,
|
||||||
onArtifactPreview,
|
onArtifactPreview,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
@@ -443,6 +446,7 @@ function AgentProgressPanel({
|
|||||||
dialogues: ReturnType<typeof useStep3Report>["dialogues"];
|
dialogues: ReturnType<typeof useStep3Report>["dialogues"];
|
||||||
error: string | null;
|
error: string | null;
|
||||||
onRetry: () => void;
|
onRetry: () => void;
|
||||||
|
onRevisionSubmit: (revisionInstruction: string) => void;
|
||||||
onArtifactStepClick: ReturnType<typeof useStep3Report>["openArtifactFromStep"];
|
onArtifactStepClick: ReturnType<typeof useStep3Report>["openArtifactFromStep"];
|
||||||
onArtifactPreview: ReturnType<typeof useStep3Report>["openArtifactPreview"];
|
onArtifactPreview: ReturnType<typeof useStep3Report>["openArtifactPreview"];
|
||||||
selectedModel: string;
|
selectedModel: string;
|
||||||
@@ -453,6 +457,15 @@ function AgentProgressPanel({
|
|||||||
const busy = phase === "creating" || phase === "streaming";
|
const busy = phase === "creating" || phase === "streaming";
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const { atBottom, scrollToBottom } = useAutoScroll(scrollRef, [dialogues]);
|
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 (
|
return (
|
||||||
<div className="flex h-full min-h-0 flex-col bg-slate-50/20">
|
<div className="flex h-full min-h-0 flex-col bg-slate-50/20">
|
||||||
@@ -549,6 +562,39 @@ function AgentProgressPanel({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!atBottom && <ScrollToBottomButton onClick={scrollToBottom} />}
|
{!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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -72,11 +72,11 @@ export interface UseStep3ReportReturn {
|
|||||||
/** 当前沙箱预览绑定的 thread id(这里恒为报告线程)。 */
|
/** 当前沙箱预览绑定的 thread id(这里恒为报告线程)。 */
|
||||||
activeArtifactThreadId: string | null;
|
activeArtifactThreadId: string | null;
|
||||||
/** 启动绘制(幂等:进行中重复调用会被忽略)。 */
|
/** 启动绘制(幂等:进行中重复调用会被忽略)。 */
|
||||||
start: () => void;
|
start: (revisionInstruction?: string) => void;
|
||||||
/** 从草稿快照恢复展示(建合成 stub + 气泡 + 打开沙箱,phase=done,不重新绘制)。 */
|
/** 从草稿快照恢复展示(建合成 stub + 气泡 + 打开沙箱,phase=done,不重新绘制)。 */
|
||||||
hydrateSaved: (snapshot: DraftStep3Snapshot) => void;
|
hydrateSaved: (snapshot: DraftStep3Snapshot) => void;
|
||||||
/** 重新绘制(先中断在途请求再重跑)。 */
|
/** 重新绘制(先中断在途请求再重跑)。 */
|
||||||
redraw: () => void;
|
redraw: (revisionInstruction?: string) => void;
|
||||||
/** 中断在途请求(卸载 / 切回普通视图时调用)。 */
|
/** 中断在途请求(卸载 / 切回普通视图时调用)。 */
|
||||||
abort: () => void;
|
abort: () => void;
|
||||||
/** 点击气泡内工具步骤时打开沙箱(与第 2 步 openArtifactFromStep 同义)。 */
|
/** 点击气泡内工具步骤时打开沙箱(与第 2 步 openArtifactFromStep 同义)。 */
|
||||||
@@ -92,8 +92,10 @@ function buildReportPrompt(opts: {
|
|||||||
intentReady: ReportIntent | null;
|
intentReady: ReportIntent | null;
|
||||||
seatDeliveries: ReportSeatDelivery[];
|
seatDeliveries: ReportSeatDelivery[];
|
||||||
consensusText: string;
|
consensusText: string;
|
||||||
|
revisionInstruction?: string;
|
||||||
|
previousHtml?: string;
|
||||||
}): string {
|
}): string {
|
||||||
const { intentReady, seatDeliveries, consensusText } = opts;
|
const { intentReady, seatDeliveries, consensusText, revisionInstruction, previousHtml } = opts;
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
parts.push(
|
parts.push(
|
||||||
"请基于以下圆桌多智能体研讨的成果,撰写一份图文并茂、以文字叙述为主线的 HTML「方案研讨汇报」," +
|
"请基于以下圆桌多智能体研讨的成果,撰写一份图文并茂、以文字叙述为主线的 HTML「方案研讨汇报」," +
|
||||||
@@ -135,6 +137,23 @@ function buildReportPrompt(opts: {
|
|||||||
parts.push("");
|
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(
|
parts.push(
|
||||||
"请严格遵循你的职责设定:自包含离线(纯 inline SVG 图表、禁止任何外链 CDN)、研讨叙事型结构" +
|
"请严格遵循你的职责设定:自包含离线(纯 inline SVG 图表、禁止任何外链 CDN)、研讨叙事型结构" +
|
||||||
"(结论+KPI → 背景目标 → 各席位观点 → 综合分析 → 最终共识方案 → 执行路线)、" +
|
"(结论+KPI → 背景目标 → 各席位观点 → 综合分析 → 最终共识方案 → 执行路线)、" +
|
||||||
@@ -173,7 +192,7 @@ function reportPhaseLabel(phase: StreamPhase, toolName?: string): string {
|
|||||||
export function useStep3Report(
|
export function useStep3Report(
|
||||||
options: UseStep3ReportOptions,
|
options: UseStep3ReportOptions,
|
||||||
): UseStep3ReportReturn {
|
): UseStep3ReportReturn {
|
||||||
const { intentReady, seatDeliveries, consensusText, selectedModel, onReportSaved } =
|
const { intentReady, seatDeliveries, consensusText, selectedModel, savedReport, onReportSaved } =
|
||||||
options;
|
options;
|
||||||
|
|
||||||
// onReportSaved 身份可能每次 render 变 —— 打到 ref,避免进 run() 的 deps 反复重建。
|
// onReportSaved 身份可能每次 render 变 —— 打到 ref,避免进 run() 的 deps 反复重建。
|
||||||
@@ -601,7 +620,7 @@ export function useStep3Report(
|
|||||||
runningRef.current = false;
|
runningRef.current = false;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const run = useCallback(async () => {
|
const run = useCallback(async (revisionInstruction?: string) => {
|
||||||
if (runningRef.current) return;
|
if (runningRef.current) return;
|
||||||
runningRef.current = true;
|
runningRef.current = true;
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -632,7 +651,13 @@ export function useStep3Report(
|
|||||||
);
|
);
|
||||||
setPhase("streaming");
|
setPhase("streaming");
|
||||||
|
|
||||||
const prompt = buildReportPrompt({ intentReady, seatDeliveries, consensusText });
|
const prompt = buildReportPrompt({
|
||||||
|
intentReady,
|
||||||
|
seatDeliveries,
|
||||||
|
consensusText,
|
||||||
|
revisionInstruction,
|
||||||
|
previousHtml: savedReport?.html,
|
||||||
|
});
|
||||||
const res = await streamMultiAgent({
|
const res = await streamMultiAgent({
|
||||||
agentType: "special",
|
agentType: "special",
|
||||||
agentName,
|
agentName,
|
||||||
@@ -687,6 +712,7 @@ export function useStep3Report(
|
|||||||
intentReady,
|
intentReady,
|
||||||
seatDeliveries,
|
seatDeliveries,
|
||||||
consensusText,
|
consensusText,
|
||||||
|
savedReport?.html,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
appendStreamingDialogue,
|
appendStreamingDialogue,
|
||||||
appendToDialogue,
|
appendToDialogue,
|
||||||
@@ -699,8 +725,8 @@ export function useStep3Report(
|
|||||||
reconcileStubFromRealFile,
|
reconcileStubFromRealFile,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const start = useCallback(() => {
|
const start = useCallback((revisionInstruction?: string) => {
|
||||||
void run();
|
void run(revisionInstruction);
|
||||||
}, [run]);
|
}, [run]);
|
||||||
|
|
||||||
// 从草稿快照恢复:不跑 SSE,用保存的 HTML 重建一条「完成」气泡 + 合成 stub,
|
// 从草稿快照恢复:不跑 SSE,用保存的 HTML 重建一条「完成」气泡 + 合成 stub,
|
||||||
@@ -766,9 +792,9 @@ export function useStep3Report(
|
|||||||
[abort, buildWriteFileVirtualUrl, openArtifact],
|
[abort, buildWriteFileVirtualUrl, openArtifact],
|
||||||
);
|
);
|
||||||
|
|
||||||
const redraw = useCallback(() => {
|
const redraw = useCallback((revisionInstruction?: string) => {
|
||||||
abort();
|
abort();
|
||||||
void run();
|
void run(revisionInstruction);
|
||||||
}, [abort, run]);
|
}, [abort, run]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in new issue
Block a user