样式修改
This commit is contained in:
1 parent
13bfa719ee
commit
c8f69781e4
6 files changed
+213
-57
No files matched your search
@@ -0,0 +1,12 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.hit { fill: rgba(255, 255, 255, 0.8); opacity: 0; }
|
||||||
|
.mark { fill: #fff; }
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(-2434.135 -67.941)">
|
||||||
|
<rect class="hit" width="12" height="12" transform="translate(2434.135 67.941)"/>
|
||||||
|
<path class="mark" d="M.885,0,0,.885,3.692,4.577,4.135,5l.442-.423L8.269.885,7.385,0,4.135,3.25Z" transform="translate(2436 71.942)"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 501 B |
@@ -0,0 +1,14 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.background { fill: none; }
|
||||||
|
.mark { stroke: rgba(0, 0, 0, 0); stroke-miterlimit: 10; fill: url(#gradient); }
|
||||||
|
</style>
|
||||||
|
<linearGradient id="gradient" x1="0.277" y1="0.03" x2="0.725" y2="1.083" gradientUnits="objectBoundingBox">
|
||||||
|
<stop offset="0" stop-color="#a2e1f9"/>
|
||||||
|
<stop offset="1" stop-color="#45c3f3"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="background" width="16" height="16"/>
|
||||||
|
<path class="mark" d="M14554.5,10202.5a1,1,0,1,1,0-2h5a1,1,0,0,1,0,2Zm9.8-.482-2.95-2.157a.445.445,0,0,1-.187-.361.46.46,0,0,1,.187-.366l2.95-2.152a.45.45,0,0,1,.716.365v4.307a.44.44,0,0,1-.131.317.445.445,0,0,1-.322.136A.454.454,0,0,1,14564.3,10202.019Zm-9.8-3.521a1,1,0,1,1,0-2h5a1,1,0,1,1,0,2Zm0-4a1,1,0,1,1,0-2h10a1,1,0,1,1,0,2Z" transform="translate(-14551.5 -10189.5)"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 913 B |
@@ -62,6 +62,7 @@ import type { AgentThreadState } from "@/core/threads/types";
|
|||||||
import { textOfMessage } from "@/core/threads/utils";
|
import { textOfMessage } from "@/core/threads/utils";
|
||||||
import { desensitize } from "@/lib/desensitize";
|
import { desensitize } from "@/lib/desensitize";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import expandLogo from "@/assets/position-roundtable/expand-logo.svg";
|
||||||
import { listChains } from "@/roundtable-planning/api/chains";
|
import { listChains } from "@/roundtable-planning/api/chains";
|
||||||
import { Step1Panel } from "@/roundtable-planning/components/Step1Panel";
|
import { Step1Panel } from "@/roundtable-planning/components/Step1Panel";
|
||||||
import { PositionNodeChat } from "@/position-roundtable/components/PositionNodeChat";
|
import { PositionNodeChat } from "@/position-roundtable/components/PositionNodeChat";
|
||||||
@@ -462,6 +463,8 @@ export default function PositionRoundtablePage({
|
|||||||
const [startingChain, setStartingChain] = useState(false);
|
const [startingChain, setStartingChain] = useState(false);
|
||||||
const [rightPanelOpen, setRightPanelOpen] = useState(true);
|
const [rightPanelOpen, setRightPanelOpen] = useState(true);
|
||||||
const [rightTab, setRightTab] = useState<WorkspaceTab>("agents");
|
const [rightTab, setRightTab] = useState<WorkspaceTab>("agents");
|
||||||
|
const [taskPanelCollapsed, setTaskPanelCollapsed] = useState(false);
|
||||||
|
const [intentPanelCollapsed, setIntentPanelCollapsed] = useState(false);
|
||||||
const artifactsCtx = useArtifactsOptional();
|
const artifactsCtx = useArtifactsOptional();
|
||||||
const artifactsCtxRef = useRef(artifactsCtx);
|
const artifactsCtxRef = useRef(artifactsCtx);
|
||||||
const [sandboxArtifact, setSandboxArtifact] = useState<PositionArtifactPreview | null>(null);
|
const [sandboxArtifact, setSandboxArtifact] = useState<PositionArtifactPreview | null>(null);
|
||||||
@@ -748,6 +751,9 @@ export default function PositionRoundtablePage({
|
|||||||
}, [positionSession?.chainSnapshot, positionSession?.status, selectedChain]);
|
}, [positionSession?.chainSnapshot, positionSession?.status, selectedChain]);
|
||||||
const isIntelligenceDeliveryOverview =
|
const isIntelligenceDeliveryOverview =
|
||||||
activePosition.isIntentPosition && positionSession !== null && positionSession.status !== "intent_pending";
|
activePosition.isIntentPosition && positionSession !== null && positionSession.status !== "intent_pending";
|
||||||
|
const showTaskAndIntentPanels = !activePosition.isIntentPosition || isIntelligenceDeliveryOverview;
|
||||||
|
const leftSidebarHidden =
|
||||||
|
showTaskAndIntentPanels && taskPanelCollapsed && intentPanelCollapsed;
|
||||||
const allPositionDeliveriesDone = useMemo(
|
const allPositionDeliveriesDone = useMemo(
|
||||||
() => allExecutableNodesDone(positionSession?.nodes ?? []),
|
() => allExecutableNodesDone(positionSession?.nodes ?? []),
|
||||||
[positionSession?.nodes],
|
[positionSession?.nodes],
|
||||||
@@ -1347,6 +1353,8 @@ export default function PositionRoundtablePage({
|
|||||||
setSelectedAgentId(null);
|
setSelectedAgentId(null);
|
||||||
setActiveNodeKey(null);
|
setActiveNodeKey(null);
|
||||||
setRightPanelOpen(nextPositionId === DEFAULT_POSITION_ID);
|
setRightPanelOpen(nextPositionId === DEFAULT_POSITION_ID);
|
||||||
|
setTaskPanelCollapsed(false);
|
||||||
|
setIntentPanelCollapsed(false);
|
||||||
},
|
},
|
||||||
[closeSandbox],
|
[closeSandbox],
|
||||||
);
|
);
|
||||||
@@ -1512,12 +1520,27 @@ export default function PositionRoundtablePage({
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="flex min-h-0 flex-1 gap-3 overflow-hidden p-3">
|
<div
|
||||||
<aside className="custom-scrollbar flex w-[360px] shrink-0 flex-col gap-3 overflow-y-auto rounded-2xl border border-slate-200 bg-white p-3 shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:text-[#c5cfe0] dark:shadow-none dark:backdrop-blur-md">
|
className={cn(
|
||||||
|
"relative flex min-h-0 flex-1 overflow-hidden p-3 transition-[gap] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
|
||||||
|
leftSidebarHidden ? "gap-0" : "gap-3",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<aside
|
||||||
|
aria-hidden={leftSidebarHidden || undefined}
|
||||||
|
className={cn(
|
||||||
|
"custom-scrollbar relative flex min-h-0 shrink-0 flex-col gap-3 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm transition-[width,opacity,transform,padding,border-width] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none dark:rounded-none dark:border-0 dark:bg-transparent dark:text-[#c5cfe0] dark:shadow-none",
|
||||||
|
leftSidebarHidden
|
||||||
|
? "w-0 -translate-x-4 !border-0 !p-0 opacity-0 pointer-events-none"
|
||||||
|
: "w-[360px] translate-x-0 p-3 opacity-100 dark:p-0",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<TaskInformationCard
|
<TaskInformationCard
|
||||||
task={task}
|
task={task}
|
||||||
editable={!isArchivedSession}
|
editable={!isArchivedSession}
|
||||||
onEdit={openTaskEditor}
|
onEdit={openTaskEditor}
|
||||||
|
collapsed={taskPanelCollapsed}
|
||||||
|
onCollapsedChange={setTaskPanelCollapsed}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{activePosition.isIntentPosition ? (
|
{activePosition.isIntentPosition ? (
|
||||||
@@ -1526,6 +1549,8 @@ export default function PositionRoundtablePage({
|
|||||||
intent={frozenIntent ?? intent.intentReady}
|
intent={frozenIntent ?? intent.intentReady}
|
||||||
editable={!isArchivedSession}
|
editable={!isArchivedSession}
|
||||||
onEdit={openIntentEditor}
|
onEdit={openIntentEditor}
|
||||||
|
collapsed={intentPanelCollapsed}
|
||||||
|
onCollapsedChange={setIntentPanelCollapsed}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<BusinessChainSelector
|
<BusinessChainSelector
|
||||||
@@ -1542,6 +1567,8 @@ export default function PositionRoundtablePage({
|
|||||||
intent={frozenIntent ?? intent.intentReady}
|
intent={frozenIntent ?? intent.intentReady}
|
||||||
editable={!isArchivedSession}
|
editable={!isArchivedSession}
|
||||||
onEdit={openIntentEditor}
|
onEdit={openIntentEditor}
|
||||||
|
collapsed={intentPanelCollapsed}
|
||||||
|
onCollapsedChange={setIntentPanelCollapsed}
|
||||||
/>
|
/>
|
||||||
<BusinessChainFlow
|
<BusinessChainFlow
|
||||||
chain={activeChain}
|
chain={activeChain}
|
||||||
@@ -1555,6 +1582,21 @@ export default function PositionRoundtablePage({
|
|||||||
)}
|
)}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
{leftSidebarHidden && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title="展开左侧任务与意图信息"
|
||||||
|
aria-label="展开左侧任务与意图信息"
|
||||||
|
onClick={() => {
|
||||||
|
setTaskPanelCollapsed(false);
|
||||||
|
setIntentPanelCollapsed(false);
|
||||||
|
}}
|
||||||
|
className="absolute left-3 top-3 z-20 flex h-10 w-[30px] items-center justify-center rounded-r-[4px] border border-l-0 border-[#2b8ffe]/60 bg-[#032650]/95 shadow-[4px_0_12px_rgba(0,0,0,0.18)] transition-colors hover:bg-[#0b386d] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/70"
|
||||||
|
>
|
||||||
|
<img src={expandLogo} alt="" className="size-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
<section className="relative flex min-w-[420px] flex-1 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:shadow-none dark:backdrop-blur-md">
|
<section className="relative flex min-w-[420px] flex-1 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:shadow-none dark:backdrop-blur-md">
|
||||||
{!isIntelligenceDeliveryOverview && !rightPanelOpen && !sandboxOpen && (
|
{!isIntelligenceDeliveryOverview && !rightPanelOpen && !sandboxOpen && (
|
||||||
<TButton
|
<TButton
|
||||||
@@ -2603,7 +2645,7 @@ function HoverEditButton({
|
|||||||
title={title}
|
title={title}
|
||||||
aria-label={title}
|
aria-label={title}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="absolute right-3 top-3 z-10 flex size-7 items-center justify-center rounded-full border border-slate-200 bg-white/95 text-slate-500 opacity-0 shadow-sm transition-all hover:border-primary/40 hover:bg-primary/5 hover:text-primary focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 group-hover:opacity-100 dark:border-[#2b8ffe]/35 dark:bg-[#063363]/95 dark:text-[#9ebfe2] dark:hover:border-[#2b8ffe]/80 dark:hover:bg-[#0b4fae]/45 dark:hover:text-[#83c1ff]"
|
className="pointer-events-none flex size-7 shrink-0 items-center justify-center rounded-[4px] text-[#45c3f3] opacity-0 transition-[opacity,color,background-color] duration-150 group-hover:pointer-events-auto group-hover:opacity-100 hover:bg-[#45c3f3]/10 hover:text-[#a2e1f9] focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/65 motion-reduce:transition-none"
|
||||||
>
|
>
|
||||||
<PencilLine className="size-3.5" strokeWidth={2.4} />
|
<PencilLine className="size-3.5" strokeWidth={2.4} />
|
||||||
</button>
|
</button>
|
||||||
@@ -2614,46 +2656,64 @@ function TaskInformationCard({
|
|||||||
task,
|
task,
|
||||||
editable,
|
editable,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
collapsed,
|
||||||
|
onCollapsedChange,
|
||||||
}: {
|
}: {
|
||||||
task: PositionTask;
|
task: PositionTask;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
onEdit?: () => void;
|
onEdit?: () => void;
|
||||||
|
collapsed: boolean;
|
||||||
|
onCollapsedChange: (collapsed: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="group relative rounded-2xl border border-[#ECE7E4] bg-white p-4 shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[#2b8ffe]/30 dark:bg-[#042b59]/55 dark:shadow-none">
|
<section className="group relative shrink-0 overflow-hidden rounded-[4px] border border-[#ECE7E4] bg-white shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[rgba(43,143,254,0.6)] dark:bg-[#032650] dark:shadow-none">
|
||||||
{editable && onEdit && <HoverEditButton title="编辑任务信息" onClick={onEdit} />}
|
<div className="relative flex h-10 items-center border-b border-slate-200 bg-primary/10 px-4 dark:border-b-[#2b8ffe]/60 dark:bg-[linear-gradient(89.65deg,#0062d9_0.03%,rgba(0,98,217,0.78)_52%,rgba(0,98,217,0)_100%)] dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||||
<div className="mb-3 flex items-center gap-2">
|
<span
|
||||||
<span className="flex size-5 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
aria-hidden="true"
|
||||||
<Target className="size-3" strokeWidth={2.5} />
|
className="mr-3 h-5 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||||
</span>
|
/>
|
||||||
<h2 className="text-sm font-semibold tracking-wide text-slate-900 dark:text-[#e8edf5]">
|
<h2 className="text-base font-semibold tracking-wide text-slate-900 dark:text-white">任务信息</h2>
|
||||||
任务信息
|
<div className="relative z-10 ml-auto flex items-center gap-2">
|
||||||
</h2>
|
{editable && onEdit && <HoverEditButton title="编辑任务信息" onClick={onEdit} />}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title={collapsed ? "展开任务信息" : "收起任务信息"}
|
||||||
|
aria-label={collapsed ? "展开任务信息" : "收起任务信息"}
|
||||||
|
aria-expanded={!collapsed}
|
||||||
|
onClick={() => onCollapsedChange(!collapsed)}
|
||||||
|
className="flex size-7 shrink-0 items-center justify-center rounded-[4px] transition-colors hover:bg-[#45c3f3]/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/65"
|
||||||
|
>
|
||||||
|
<img src={expandLogo} alt="" className="size-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div
|
||||||
<div>
|
className={cn(
|
||||||
<p className="text-[10px] font-semibold uppercase tracking-wide text-slate-400 dark:text-[#8aa0c4]">
|
"grid transition-[grid-template-rows,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
|
||||||
任务名称
|
collapsed ? "grid-rows-[0fr] opacity-0" : "grid-rows-[1fr] opacity-100",
|
||||||
</p>
|
)}
|
||||||
<p className="mt-0.5 line-clamp-2 text-xs font-semibold leading-relaxed text-slate-800 dark:text-[#e8edf5]">
|
>
|
||||||
{desensitize(task.title)}
|
<div className="min-h-0 overflow-hidden">
|
||||||
</p>
|
<div className="space-y-3 p-4">
|
||||||
</div>
|
<div>
|
||||||
<div>
|
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">任务名称</p>
|
||||||
<p className="text-[10px] font-semibold uppercase tracking-wide text-slate-400 dark:text-[#8aa0c4]">
|
<p
|
||||||
任务描述
|
title={desensitize(task.title)}
|
||||||
</p>
|
className="mt-1 line-clamp-1 text-sm font-semibold leading-relaxed text-slate-800 dark:text-[#f2f7ff]"
|
||||||
<p className="mt-0.5 line-clamp-5 whitespace-pre-wrap text-[11px] leading-relaxed text-slate-600 dark:text-[#a9c2e4]">
|
>
|
||||||
{desensitize(task.description)}
|
{desensitize(task.title)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between gap-2 border-t border-slate-100 pt-2.5 dark:border-[#2b8ffe]/20">
|
<div>
|
||||||
<span className="text-[10px] font-semibold uppercase tracking-wide text-slate-400 dark:text-[#8aa0c4]">
|
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">任务描述</p>
|
||||||
任务方向
|
<p
|
||||||
</span>
|
title={desensitize(task.description)}
|
||||||
<span className="truncate rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-semibold text-primary dark:bg-[#2b8ffe]/15 dark:text-[#83c1ff]">
|
className="mt-1 line-clamp-3 whitespace-pre-wrap text-sm leading-6 text-slate-600 dark:text-[#d8e6f8]"
|
||||||
{desensitize(task.direction)}
|
>
|
||||||
</span>
|
{desensitize(task.description)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -2930,40 +2990,83 @@ function IntentUnderstandingCard({
|
|||||||
intent,
|
intent,
|
||||||
editable,
|
editable,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
collapsed,
|
||||||
|
onCollapsedChange,
|
||||||
}: {
|
}: {
|
||||||
intent: ReturnType<typeof useStep1Intent>["intentReady"];
|
intent: ReturnType<typeof useStep1Intent>["intentReady"];
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
onEdit?: () => void;
|
onEdit?: () => void;
|
||||||
|
collapsed: boolean;
|
||||||
|
onCollapsedChange: (collapsed: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="group relative rounded-2xl border border-[#ECE7E4] bg-white p-4 shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[#2b8ffe]/30 dark:bg-[#042b59]/55 dark:shadow-none">
|
<section
|
||||||
{editable && onEdit && <HoverEditButton title="编辑意图理解" onClick={onEdit} />}
|
className={cn(
|
||||||
<h2 className="mb-3 flex items-center gap-2 text-sm font-semibold tracking-wide text-slate-900 dark:text-[#e8edf5]">
|
"group relative flex min-h-0 flex-col overflow-hidden rounded-[4px] border border-[#ECE7E4] bg-white shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[rgba(43,143,254,0.6)] dark:bg-[#032650] dark:shadow-none",
|
||||||
<span className="flex size-5 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
collapsed ? "flex-none" : "flex-1",
|
||||||
<Sparkles className="size-3" strokeWidth={2.5} />
|
)}
|
||||||
</span>
|
>
|
||||||
意图理解
|
<div className="relative flex h-10 shrink-0 items-center border-b border-slate-200 bg-primary/10 px-4 dark:border-b-[#2b8ffe]/60 dark:bg-[linear-gradient(89.65deg,#0062d9_0.03%,rgba(0,98,217,0.78)_52%,rgba(0,98,217,0)_100%)] dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||||
</h2>
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="mr-3 h-5 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||||
|
/>
|
||||||
|
<h2 className="text-base font-semibold tracking-wide text-slate-900 dark:text-white">意图理解</h2>
|
||||||
|
<div className="relative z-10 ml-auto flex items-center gap-2">
|
||||||
|
{editable && onEdit && <HoverEditButton title="编辑意图理解" onClick={onEdit} />}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title={collapsed ? "展开意图理解" : "收起意图理解"}
|
||||||
|
aria-label={collapsed ? "展开意图理解" : "收起意图理解"}
|
||||||
|
aria-expanded={!collapsed}
|
||||||
|
onClick={() => onCollapsedChange(!collapsed)}
|
||||||
|
className="flex size-7 shrink-0 items-center justify-center rounded-[4px] transition-colors hover:bg-[#45c3f3]/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/65"
|
||||||
|
>
|
||||||
|
<img src={expandLogo} alt="" className="size-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"grid min-h-0 transition-[grid-template-rows,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
|
||||||
|
collapsed ? "grid-rows-[0fr] opacity-0" : "grid-rows-[1fr] opacity-100",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="min-h-0 overflow-hidden">
|
||||||
{intent ? (
|
{intent ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3 p-4">
|
||||||
<p className="text-xs font-medium leading-5 text-slate-800 dark:text-[#e8edf5]">{desensitize(intent.objective)}</p>
|
<div>
|
||||||
<div className="space-y-1.5">
|
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">任务意图</p>
|
||||||
{intent.constraints.map((constraint, index) => (
|
<p className="mt-1 text-sm leading-6 text-slate-700 dark:text-[#f2f7ff]">
|
||||||
<div
|
{desensitize(intent.objective)}
|
||||||
key={`${constraint}-${index}`}
|
</p>
|
||||||
className="flex items-start gap-2 text-[11px] text-muted-foreground dark:text-[#a9c2e4]"
|
|
||||||
>
|
|
||||||
<CheckCircle2 className="mt-0.5 size-3 shrink-0 text-emerald-500" />
|
|
||||||
<span className="leading-4">{desensitize(constraint)}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">关键约束</p>
|
||||||
|
<ul className="mt-1 list-disc space-y-1 pl-4 text-sm leading-6 text-slate-600 marker:text-slate-400 dark:text-[#d8e6f8] dark:marker:text-[#83b6ed]">
|
||||||
|
{intent.constraints.map((constraint, index) => (
|
||||||
|
<li key={`${constraint}-${index}`}>{desensitize(constraint)}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{intent.assumptions && intent.assumptions.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">关键假设</p>
|
||||||
|
<ul className="mt-1 list-disc space-y-1 pl-4 text-sm leading-6 text-slate-600 marker:text-slate-400 dark:text-[#d8e6f8] dark:marker:text-[#83b6ed]">
|
||||||
|
{intent.assumptions.map((assumption, index) => (
|
||||||
|
<li key={`${assumption}-${index}`}>{desensitize(assumption)}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl border border-dashed border-[#E8D0CC] p-3 text-xs leading-5 text-muted-foreground dark:border-[#2b8ffe]/30 dark:text-[#8aa0c4]">
|
<div className="p-4 text-sm leading-6 text-muted-foreground dark:text-[#d8e6f8]">
|
||||||
情报分析岗完成意图识别后,其他岗位会在这里看到统一的任务意图与约束。
|
情报分析岗完成意图识别后,其他岗位会在这里看到统一的任务意图与约束。
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -942,6 +942,14 @@ make gateway
|
|||||||
Direct access (without nginx):
|
Direct access (without nginx):
|
||||||
- Gateway: `http://localhost:8001`
|
- Gateway: `http://localhost:8001`
|
||||||
|
|
||||||
|
### Browser CORS
|
||||||
|
|
||||||
|
The Gateway always permits the local TaskCOP/Vite origins
|
||||||
|
`http://localhost:8080` and `http://127.0.0.1:8080` with credentials. A
|
||||||
|
deployed frontend on another origin must be listed exactly in
|
||||||
|
`GATEWAY_CORS_ORIGINS` (for example `http://47.88.25.99:7010`); those explicit
|
||||||
|
origins are merged with the local TaskCOP origins.
|
||||||
|
|
||||||
### Frontend Configuration
|
### Frontend Configuration
|
||||||
|
|
||||||
The frontend uses environment variables to connect to backend services:
|
The frontend uses environment variables to connect to backend services:
|
||||||
|
|||||||
@@ -285,6 +285,14 @@ Provider note:
|
|||||||
- `models[*].use` references provider classes by module path (for example `langchain_openai:ChatOpenAI`).
|
- `models[*].use` references provider classes by module path (for example `langchain_openai:ChatOpenAI`).
|
||||||
- If a provider module is missing, DeerFlow now returns an actionable error with install guidance (for example `uv add langchain-google-genai`).
|
- If a provider module is missing, DeerFlow now returns an actionable error with install guidance (for example `uv add langchain-google-genai`).
|
||||||
|
|
||||||
|
### Browser CORS
|
||||||
|
|
||||||
|
The Gateway permits the local TaskCOP/Vite development origins
|
||||||
|
`http://localhost:8080` and `http://127.0.0.1:8080` with credentials. For a
|
||||||
|
separate deployed frontend, set `GATEWAY_CORS_ORIGINS` to its exact origin
|
||||||
|
(including scheme and port), for example `http://47.88.25.99:7010`. Explicit
|
||||||
|
origins are used in addition to the local TaskCOP development origins.
|
||||||
|
|
||||||
### Extensions Configuration (`extensions_config.json`)
|
### Extensions Configuration (`extensions_config.json`)
|
||||||
|
|
||||||
MCP servers and skill states in a single file:
|
MCP servers and skill states in a single file:
|
||||||
|
|||||||
@@ -1328,8 +1328,18 @@ This gateway provides custom endpoints for models, MCP configuration, skills, an
|
|||||||
v = os.environ.get("GATEWAY_CORS_ALLOW_ALL", "").strip().lower()
|
v = os.environ.get("GATEWAY_CORS_ALLOW_ALL", "").strip().lower()
|
||||||
return v in ("1", "true", "yes", "on") or is_auth_disabled()
|
return v in ("1", "true", "yes", "on") or is_auth_disabled()
|
||||||
|
|
||||||
|
# The legacy TaskCOP Vue application uses Vite's default development port.
|
||||||
|
# Keep these origins available even when deployments configure additional
|
||||||
|
# origins through GATEWAY_CORS_ORIGINS.
|
||||||
|
taskcop_vite_origins = [
|
||||||
|
"http://localhost:8080",
|
||||||
|
"http://127.0.0.1:8080",
|
||||||
|
]
|
||||||
|
|
||||||
cors_origins_env = os.environ.get("GATEWAY_CORS_ORIGINS", "").strip()
|
cors_origins_env = os.environ.get("GATEWAY_CORS_ORIGINS", "").strip()
|
||||||
cors_origins: list[str] = [o.strip() for o in cors_origins_env.split(",") if o.strip()] if cors_origins_env else []
|
cors_origins: list[str] = [o.strip() for o in cors_origins_env.split(",") if o.strip()] if cors_origins_env else []
|
||||||
|
if cors_origins:
|
||||||
|
cors_origins = list(dict.fromkeys([*cors_origins, *taskcop_vite_origins]))
|
||||||
|
|
||||||
# Headers the LangGraph SDK / browser clients need to read from cross-origin
|
# Headers the LangGraph SDK / browser clients need to read from cross-origin
|
||||||
# responses. Without exposing them, the SDK's `response.headers.get(...)` returns
|
# responses. Without exposing them, the SDK's `response.headers.get(...)` returns
|
||||||
@@ -1384,6 +1394,7 @@ This gateway provides custom endpoints for models, MCP configuration, skills, an
|
|||||||
"http://127.0.0.1:5174",
|
"http://127.0.0.1:5174",
|
||||||
"http://localhost:3000",
|
"http://localhost:3000",
|
||||||
"http://127.0.0.1:3000",
|
"http://127.0.0.1:3000",
|
||||||
|
*taskcop_vite_origins,
|
||||||
]
|
]
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"No CORS configuration found; enabling local dev origins only: %s. "
|
"No CORS configuration found; enabling local dev origins only: %s. "
|
||||||
|
|||||||
Reference in new issue
Block a user