refactor: remove lite-plan-c workflow and update orchestrator UI components

- Deleted the lite-plan-c workflow file to streamline the planning process.
- Updated orchestrator localization files to include new variable picker and multi-node selector messages.
- Modified PropertyPanel to support new execution modes and added output variable input fields.
- Enhanced SlashCommandNode to reflect changes in execution modes.
- Introduced MultiNodeSelector component for better node selection management.
- Added VariablePicker component for selecting variables with improved UX.
This commit is contained in:
catlog22
2026-02-03 21:24:34 +08:00
parent 9bb50a13fa
commit a806d70d9b
13 changed files with 233 additions and 3084 deletions

View File

@@ -0,0 +1,123 @@
import * as React from "react";
import { useIntl } from "react-intl";
import { Check, X } from "lucide-react";
import { cn } from "@/lib/utils";
export interface NodeOption {
id: string;
label: string;
type?: string;
}
export interface MultiNodeSelectorProps {
availableNodes: NodeOption[];
selectedNodes: string[];
onChange: (selectedIds: string[]) => void;
placeholder?: string;
emptyMessage?: string;
className?: string;
}
const MultiNodeSelector = React.forwardRef<HTMLDivElement, MultiNodeSelectorProps>(
({ availableNodes, selectedNodes, onChange, placeholder, emptyMessage, className }, ref) => {
const { formatMessage } = useIntl();
const isSelected = (nodeId: string) => selectedNodes.includes(nodeId);
const toggleNode = (nodeId: string) => {
if (isSelected(nodeId)) {
onChange(selectedNodes.filter((id) => id !== nodeId));
} else {
onChange([...selectedNodes, nodeId]);
}
};
const clearSelection = () => {
onChange([]);
};
return (
<div ref={ref} className={cn("space-y-2", className)}>
{/* Selected tags */}
{selectedNodes.length > 0 && (
<div className="flex flex-wrap gap-2 p-2 rounded-md border border-border bg-muted/30">
{selectedNodes.map((nodeId) => {
const node = availableNodes.find((n) => n.id === nodeId);
return (
<span
key={nodeId}
className="inline-flex items-center gap-1 px-2 py-1 rounded-md bg-primary text-primary-foreground text-xs"
>
<span>{node?.label || nodeId}</span>
<button
type="button"
onClick={() => toggleNode(nodeId)}
className="hover:bg-primary-foreground/20 rounded p-0.5"
>
<X className="w-3 h-3" />
</button>
</span>
);
})}
<button
type="button"
onClick={clearSelection}
className="text-xs text-muted-foreground hover:text-foreground underline"
>
{formatMessage({ id: 'orchestrator.multiNodeSelector.clear' })}
</button>
</div>
)}
{/* Available nodes list */}
<div className="border border-border rounded-md bg-background max-h-48 overflow-y-auto">
{availableNodes.length === 0 ? (
<div className="p-4 text-sm text-muted-foreground text-center">
{emptyMessage || formatMessage({ id: 'orchestrator.multiNodeSelector.empty' })}
</div>
) : (
<div className="p-1">
{availableNodes.map((node) => (
<div
key={node.id}
onClick={() => toggleNode(node.id)}
className={cn(
"flex items-center gap-2 p-2 rounded cursor-pointer transition-colors",
"hover:bg-muted",
isSelected(node.id) && "bg-muted"
)}
>
<div
className={cn(
"w-4 h-4 rounded border flex items-center justify-center",
isSelected(node.id)
? "bg-primary border-primary"
: "border-border"
)}
>
{isSelected(node.id) && (
<Check className="w-3 h-3 text-primary-foreground" />
)}
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-foreground truncate">
{node.label}
</div>
{node.type && (
<div className="text-xs text-muted-foreground">
{node.type}
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}
);
MultiNodeSelector.displayName = "MultiNodeSelector";
export { MultiNodeSelector };

View File

@@ -0,0 +1,54 @@
import * as React from "react";
import { useIntl } from "react-intl";
import { cn } from "@/lib/utils";
export interface VariablePickerProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'onChange'> {
options: string[];
value?: string;
onChange?: (value: string) => void;
placeholder?: string;
emptyMessage?: string;
}
const VariablePicker = React.forwardRef<HTMLSelectElement, VariablePickerProps>(
({ className, options, value, onChange, placeholder, emptyMessage, ...props }, ref) => {
const { formatMessage } = useIntl();
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
onChange?.(e.target.value);
};
return (
<select
ref={ref}
value={value || ''}
onChange={handleChange}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
{placeholder && (
<option value="">
{placeholder}
</option>
)}
{options.length === 0 ? (
<option value="" disabled>
{emptyMessage || formatMessage({ id: 'orchestrator.variablePicker.empty' })}
</option>
) : (
options.map((option) => (
<option key={option} value={option}>
{option}
</option>
))
)}
</select>
);
}
);
VariablePicker.displayName = "VariablePicker";
export { VariablePicker };