improve workflow timeline and resize controls
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-26 21:01:10 +08:00
parent 399abb1ed3
commit b088d65a66
2 changed files with 167 additions and 64 deletions

View File

@ -156,6 +156,14 @@ function getWorkflowStepOptionLevels(params: {
return levels;
}
function getWorkflowOptionDepthLabel(depth: number) {
return depth === 1 ? "主卡片" : `${depth}级子卡片`;
}
function getWorkflowOptionDepthShortLabel(depth: number) {
return depth === 1 ? "主" : `${depth}级`;
}
function StepOptionCard({
option,
step,
@ -695,7 +703,10 @@ export function AgentWorkflowPanelDarkV2({
selectionValue: selections[step.key],
});
const active = step.key === activeEmbeddedStepEntry.step.key;
const stepSummary = stepSelectionSummaryMap[step.key] || "";
const stepSelectedPath = getAgentWorkflowSelectedOptionPath({
step,
selectionValue: selections[step.key],
});
const stepOptionLevels = getWorkflowStepOptionLevels({
step,
selectionValue: selections[step.key],
@ -707,9 +718,9 @@ export function AgentWorkflowPanelDarkV2({
const activeStepChildLabel =
active && stepOptionLevels.length > 1
? `${
activeEmbeddedOptionLevel?.depth === 1
? "主卡片"
: `${activeEmbeddedOptionLevel?.depth || 1}级子卡片`
getWorkflowOptionDepthLabel(
activeEmbeddedOptionLevel?.depth || 1
)
}`
: deepestSelectedLevel > 1
? `已到 ${deepestSelectedLevel} 级`
@ -717,68 +728,102 @@ export function AgentWorkflowPanelDarkV2({
return (
<Fragment key={step.key}>
<button
type="button"
onClick={() => {
setPendingAutoAdvanceStepKey(null);
setAutoAdvanceNotice(null);
setActiveEmbeddedStepKey(step.key);
}}
className="group relative flex min-w-0 flex-col items-start pr-2 text-left transition-transform duration-200 hover:-translate-y-[1px]"
>
<div className="flex w-full items-center gap-3">
<span
className={cn(
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-[11px] font-semibold transition-colors",
active
? "border-emerald-300 bg-emerald-300 text-black shadow-[0_0_18px_rgba(52,211,153,0.28)]"
: completed
? "border-white/30 bg-white text-black"
: "border-white/12 bg-white/[0.04] text-white/62 group-hover:border-white/24 group-hover:text-white/82"
)}
>
{stepIndex + 1}
</span>
<span
className={cn(
"truncate text-[13px] font-semibold transition-colors",
active
? "text-white"
: completed
? "text-white/82"
: "text-white/52 group-hover:text-white/76"
)}
>
{step.title}
</span>
{active ? (
<span className="rounded-full border border-emerald-300/30 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-medium text-emerald-200">
当前步骤
<div className="group relative flex min-w-0 flex-col items-start pr-2 text-left">
<button
type="button"
onClick={() => {
setPendingAutoAdvanceStepKey(null);
setAutoAdvanceNotice(null);
setActiveEmbeddedStepKey(step.key);
setActiveEmbeddedOptionDepth(1);
}}
className="flex w-full flex-col items-start text-left transition-transform duration-200 hover:-translate-y-[1px]"
>
<div className="flex w-full items-center gap-3">
<span
className={cn(
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-[11px] font-semibold transition-colors",
active
? "border-emerald-300 bg-emerald-300 text-black shadow-[0_0_18px_rgba(52,211,153,0.28)]"
: completed
? "border-white/30 bg-white text-black"
: "border-white/12 bg-white/[0.04] text-white/62 group-hover:border-white/24 group-hover:text-white/82"
)}
>
{stepIndex + 1}
</span>
<span
className={cn(
"truncate text-[13px] font-semibold transition-colors",
active
? "text-white"
: completed
? "text-white/82"
: "text-white/52 group-hover:text-white/76"
)}
>
{step.title}
</span>
{active ? (
<span className="rounded-full border border-emerald-300/30 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-medium text-emerald-200">
当前步骤
</span>
) : null}
</div>
{activeStepChildLabel ? (
<span
className={cn(
"mt-1.5 pl-10 text-[10px] leading-4",
active ? "text-emerald-100/78" : "text-white/28"
)}
>
{activeStepChildLabel}
</span>
) : null}
</div>
<span
className={cn(
"mt-1.5 pl-10 text-[10px] leading-4",
active
? "text-emerald-100/78"
: completed
? "text-white/50"
: "text-white/32"
)}
>
{stepSummary || "待选择"}
</span>
{activeStepChildLabel ? (
<span
className={cn(
"mt-1 pl-10 text-[10px] leading-4",
active ? "text-emerald-200/72" : "text-white/28"
)}
>
{activeStepChildLabel}
</button>
{stepSelectedPath.length > 0 ? (
<div className="mt-2 flex flex-wrap gap-1.5 pl-10">
{stepSelectedPath.map((option, optionIndex) => {
const optionDepth = optionIndex + 1;
const optionLevelActive =
active &&
(activeEmbeddedOptionLevel?.depth || 1) === optionDepth;
return (
<button
key={`${step.key}-${option.key}-${optionDepth}`}
type="button"
onClick={() => {
setPendingAutoAdvanceStepKey(null);
setAutoAdvanceNotice(null);
setActiveEmbeddedStepKey(step.key);
setActiveEmbeddedOptionDepth(optionDepth);
}}
className={cn(
"inline-flex max-w-full items-center gap-1 rounded-full border px-2 py-1 text-[10px] leading-none transition-colors",
optionLevelActive
? "border-emerald-300/35 bg-emerald-400/12 text-emerald-100"
: active || completed
? "border-white/14 bg-white/[0.06] text-white/72 hover:border-white/22 hover:bg-white/[0.09] hover:text-white"
: "border-white/10 bg-white/[0.03] text-white/48 hover:border-white/18 hover:text-white/72"
)}
title={`返回${getWorkflowOptionDepthLabel(optionDepth)}:${option.buttonName}`}
>
<span className="text-[9px] opacity-70">
{getWorkflowOptionDepthShortLabel(optionDepth)}
</span>
<span className="max-w-[9rem] truncate">
{option.buttonName}
</span>
</button>
);
})}
</div>
) : (
<span className="mt-1.5 pl-10 text-[10px] leading-4 text-white/32">
待选择
</span>
) : null}
)}
{stepIndex < flattenedResolvedSteps.length - 1 ? (
<div
className={cn(
@ -789,7 +834,7 @@ export function AgentWorkflowPanelDarkV2({
)}
/>
) : null}
</button>
</div>
</Fragment>
);
})}

View File

@ -350,6 +350,10 @@ export function ImageAnnotator({
{ label: "4:5", value: "4:5", ratio: 4 / 5 },
{ label: "21:9", value: "21:9", ratio: 21 / 9 },
];
const resizeQuickPresets = [
{ label: "主图 1440×1440", width: 1440, height: 1440, aspectRatioValue: "1:1" },
{ label: "主图 1440×1920", width: 1440, height: 1920, aspectRatioValue: "3:4" },
] as const;
// 根据比例和宽度计算高度
const calculateHeightFromWidth = (width: number, ratio: number | null) => {
@ -396,6 +400,14 @@ export function ImageAnnotator({
}
};
const handleResizeQuickPresetSelect = (
preset: (typeof resizeQuickPresets)[number]
) => {
setResizeCanvasWidth(preset.width);
setResizeCanvasHeight(preset.height);
setSelectedAspectRatio(preset.aspectRatioValue);
};
// 确保在客户端渲染
useEffect(() => {
setMounted(true);
@ -1019,6 +1031,14 @@ export function ImageAnnotator({
setResizeImageScale(newScale);
};
const handleResizeScaleSliderChange = (value: number[]) => {
const nextScale = value[0];
if (!isResizeMode || typeof nextScale !== "number" || Number.isNaN(nextScale)) {
return;
}
setResizeImageScale(Math.max(0.1, Math.min(5, nextScale)));
};
// 尺寸调整:渲染预览
useEffect(() => {
if (!isResizeMode || !resizeCanvasRef.current || !image || !resizeContainerRef.current) return;
@ -1754,6 +1774,44 @@ export function ImageAnnotator({
/>
</div>
<div className="h-7 w-px shrink-0 bg-border/60" />
<div className="flex items-center gap-1 rounded-[22px] bg-muted/35 px-1 py-1">
{resizeQuickPresets.map((preset) => {
const isActive =
resizeCanvasWidth === preset.width && resizeCanvasHeight === preset.height;
return (
<Button
key={`${preset.width}x${preset.height}`}
variant={isActive ? "secondary" : "ghost"}
size="sm"
onClick={() => handleResizeQuickPresetSelect(preset)}
className={cn(
"h-8 rounded-[18px] px-3 text-xs font-medium whitespace-nowrap",
isActive && "shadow-sm"
)}
>
{preset.label}
</Button>
);
})}
</div>
<div className="h-7 w-px shrink-0 bg-border/60" />
<div className="flex min-w-[220px] items-center gap-3 rounded-[22px] bg-muted/35 px-3 py-2">
<span className="shrink-0 text-xs font-medium text-muted-foreground">
缩放
</span>
<Slider
value={[resizeImageScale]}
onValueChange={handleResizeScaleSliderChange}
min={0.1}
max={5}
step={0.01}
className="flex-1"
/>
<span className="min-w-[48px] shrink-0 text-right text-xs font-medium text-foreground">
{(resizeImageScale * 100).toFixed(0)}%
</span>
</div>
<div className="h-7 w-px shrink-0 bg-border/60" />
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>