Skip to content

Commit dfd6535

Browse files
committed
Upgraded UI/UX with better rendering and Memory Liks Fixed
1 parent 1cf0179 commit dfd6535

8 files changed

Lines changed: 239 additions & 105 deletions

File tree

frontend/__tests__/builder/edgeValidation.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, it } from "vitest";
22
import type { Connection } from "@xyflow/react";
3-
import { validateConnectionCandidate } from "@/hooks/usePipelineEditor";
3+
import { removeEdgeById, validateConnectionCandidate } from "@/hooks/usePipelineEditor";
44
import type { BuilderEdge, BuilderNode } from "@/lib/yamlGraphSync";
55

66
function makeConnection(source: string, target: string, targetHandle?: string): Connection {
@@ -72,3 +72,18 @@ describe("validateConnectionCandidate", () => {
7272
expect(result.message).toContain("terminal");
7373
});
7474
});
75+
76+
describe("removeEdgeById", () => {
77+
it("removes only the targeted edge id", () => {
78+
const edges: BuilderEdge[] = [
79+
{ id: "e-1", source: "load", target: "filter" },
80+
{ id: "e-2", source: "filter", target: "save" },
81+
{ id: "e-3", source: "load", target: "save" },
82+
];
83+
84+
const nextEdges = removeEdgeById(edges, "e-2");
85+
86+
expect(nextEdges).toHaveLength(2);
87+
expect(nextEdges.map((edge) => edge.id)).toEqual(["e-1", "e-3"]);
88+
});
89+
});

frontend/components/pipeline-builder/ConfigPanel.tsx

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,15 @@ export function ConfigPanel({
129129
};
130130

131131
return (
132-
<aside className="w-80 shrink-0 border-l bg-card/40 p-3" data-testid="config-panel">
132+
<aside
133+
className="flex h-full w-72 shrink-0 flex-col border-l bg-[var(--bg-surface)] p-3 text-[var(--text-primary)]"
134+
style={{ borderColor: "var(--widget-border)" }}
135+
data-testid="config-panel"
136+
>
133137
<div className="mb-3 flex items-start justify-between gap-2">
134138
<div>
135-
<h4 className="text-sm font-semibold">{node.data.label}</h4>
136-
<p className="text-xs text-muted-foreground">{stepDefinition.label} configuration</p>
139+
<h4 className="text-sm font-semibold text-[var(--text-primary)]">{node.data.label}</h4>
140+
<p className="text-xs text-[var(--text-secondary)]">{stepDefinition.label} configuration</p>
137141
</div>
138142
<div className="flex items-center gap-1">
139143
<button
@@ -143,22 +147,26 @@ export function ConfigPanel({
143147
onClose();
144148
}}
145149
data-testid="config-panel-delete"
146-
className="rounded border border-destructive/30 px-2 py-1 text-xs text-destructive hover:bg-destructive/10"
150+
className="rounded border px-2 py-1 text-xs text-[var(--accent-error)] transition-colors hover:bg-[var(--interactive-hover)]"
151+
style={{ borderColor: "color-mix(in srgb, var(--accent-error) 45%, transparent)" }}
147152
>
148153
Delete
149154
</button>
150155
<button
151156
type="button"
152157
onClick={onClose}
153158
data-testid="config-panel-close"
154-
className="rounded border px-2 py-1 text-xs hover:bg-muted"
159+
className="rounded border px-2 py-1 text-xs transition-colors hover:bg-[var(--interactive-hover)]"
160+
style={{ borderColor: "var(--widget-border)" }}
155161
>
156162
Close
157163
</button>
158164
</div>
159165
</div>
160166

161-
<div className="space-y-3 text-xs">
167+
<div
168+
className="flex-1 space-y-3 overflow-y-auto pr-1 text-xs [&_input]:border-[var(--widget-border)] [&_input]:bg-[var(--bg-base)] [&_input]:text-[var(--text-primary)] [&_input:focus]:border-[var(--widget-border)] [&_input:focus]:outline-none [&_input:focus]:shadow-none [&_input:focus-visible]:outline-none [&_input:focus-visible]:shadow-none [&_select]:border-[var(--widget-border)] [&_select]:bg-[var(--bg-base)] [&_select]:text-[var(--text-primary)] [&_select:focus]:border-[var(--widget-border)] [&_select:focus]:outline-none [&_select:focus]:shadow-none [&_select:focus-visible]:outline-none [&_select:focus-visible]:shadow-none [&_textarea]:border-[var(--widget-border)] [&_textarea]:bg-[var(--bg-base)] [&_textarea]:text-[var(--text-primary)] [&_textarea:focus]:border-[var(--widget-border)] [&_textarea:focus]:outline-none [&_textarea:focus]:shadow-none [&_textarea:focus-visible]:outline-none [&_textarea:focus-visible]:shadow-none [&_option]:bg-[var(--bg-surface)] [&_option]:text-[var(--text-primary)]"
169+
>
162170
{node.data.type === "load" && (
163171
<label className="block space-y-1">
164172
<span className="text-muted-foreground">File</span>
@@ -676,20 +684,28 @@ export function ConfigPanel({
676684
)}
677685
</div>
678686

679-
<div className="mt-4 flex items-center justify-end gap-2">
687+
<div
688+
className="mt-3 flex items-center justify-end gap-2 border-t pt-3"
689+
style={{ borderColor: "var(--widget-border)" }}
690+
>
680691
<button
681692
type="button"
682693
onClick={onClose}
683694
data-testid="config-panel-cancel"
684-
className="rounded border px-2.5 py-1.5 text-xs hover:bg-muted"
695+
className="rounded border px-2.5 py-1.5 text-xs transition-colors hover:bg-[var(--interactive-hover)]"
696+
style={{ borderColor: "var(--widget-border)" }}
685697
>
686698
Cancel
687699
</button>
688700
<button
689701
type="button"
690702
onClick={saveConfig}
691703
data-testid="config-panel-save"
692-
className="rounded border border-primary bg-primary px-2.5 py-1.5 text-xs text-primary-foreground hover:opacity-90"
704+
className="rounded border px-2.5 py-1.5 text-xs font-medium text-[var(--bg-base)] transition-[filter] hover:brightness-110"
705+
style={{
706+
borderColor: "color-mix(in srgb, var(--accent-primary) 60%, transparent)",
707+
backgroundColor: "var(--accent-primary)",
708+
}}
693709
>
694710
Save
695711
</button>

frontend/components/pipeline-builder/PipelineCanvas.tsx

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface PipelineCanvasProps {
2525
onDropStep: (stepType: VisualStepType, position: XYPosition) => void;
2626
onConfigureNode: (nodeId: string) => void;
2727
onDeleteNode: (nodeId: string) => void;
28+
onDeleteEdge: (edgeId: string) => void;
2829
}
2930

3031
const nodeTypes = {
@@ -40,6 +41,7 @@ export function PipelineCanvas({
4041
onDropStep,
4142
onConfigureNode,
4243
onDeleteNode,
44+
onDeleteEdge,
4345
}: PipelineCanvasProps) {
4446
const [instance, setInstance] = useState<ReactFlowInstance<BuilderNode, BuilderEdge> | null>(
4547
null,
@@ -88,13 +90,22 @@ export function PipelineCanvas({
8890
);
8991

9092
return (
91-
<div className="relative h-full w-full rounded-md border bg-background/30" data-testid="pipeline-canvas">
93+
<div
94+
className="relative h-full w-full overflow-hidden rounded-md border bg-[var(--bg-base)]"
95+
style={{ borderColor: "var(--widget-border)" }}
96+
data-testid="pipeline-canvas"
97+
>
9298
<ReactFlow<BuilderNode, BuilderEdge>
9399
nodes={decoratedNodes}
94100
edges={edges}
95101
onNodesChange={onNodesChange}
96102
onEdgesChange={onEdgesChange}
97103
onConnect={onConnect}
104+
onEdgeDoubleClick={(event, edge) => {
105+
event.preventDefault();
106+
event.stopPropagation();
107+
onDeleteEdge(edge.id);
108+
}}
98109
onInit={setInstance}
99110
onDragOver={handleDragOver}
100111
onDrop={handleDrop}
@@ -103,13 +114,25 @@ export function PipelineCanvas({
103114
minZoom={0.2}
104115
maxZoom={2.5}
105116
proOptions={{ hideAttribution: true }}
117+
className="bg-[var(--bg-base)]"
118+
style={{ background: "var(--bg-base)" }}
119+
defaultEdgeOptions={{
120+
style: {
121+
stroke: "var(--accent-primary)",
122+
strokeOpacity: 0.6,
123+
strokeWidth: 1.5,
124+
},
125+
}}
106126
>
107-
<Background variant={BackgroundVariant.Dots} gap={20} size={1.2} />
108-
<Controls />
127+
<Background variant={BackgroundVariant.Dots} gap={16} size={1.2} color="var(--widget-border)" />
128+
<Controls className="!border-[var(--widget-border)] !bg-[var(--bg-surface)] !fill-[var(--text-primary)]" />
109129
<MiniMap
110-
nodeColor="#64748b"
130+
nodeColor="var(--text-secondary)"
111131
maskColor="rgba(15, 23, 42, 0.4)"
112-
style={{ background: "var(--card)", border: "1px solid var(--border)" }}
132+
style={{
133+
background: "var(--bg-surface)",
134+
border: "1px solid var(--widget-border)",
135+
}}
113136
/>
114137
</ReactFlow>
115138

@@ -118,7 +141,13 @@ export function PipelineCanvas({
118141
className="pointer-events-none absolute inset-0 flex items-center justify-center"
119142
data-testid="pipeline-canvas-empty"
120143
>
121-
<p className="rounded border bg-card px-3 py-1.5 text-xs text-muted-foreground">
144+
<p
145+
className="rounded border px-3 py-1.5 text-xs text-[var(--text-secondary)]"
146+
style={{
147+
borderColor: "var(--widget-border)",
148+
backgroundColor: "var(--bg-elevated)",
149+
}}
150+
>
122151
Drag steps from the palette to start building.
123152
</p>
124153
</div>

frontend/components/pipeline-builder/StepNode.tsx

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,22 @@ function StepNodeComponent({ id, data, selected }: NodeProps<BuilderNode>) {
1010
<div
1111
data-testid={`step-node-${id}`}
1212
className={[
13-
"min-w-[190px] rounded-lg border bg-card px-3 py-2 text-card-foreground shadow-sm",
14-
selected ? "ring-2 ring-primary" : "ring-0",
13+
"min-w-[190px] rounded-lg border px-3 py-2 shadow-sm transition-all",
14+
selected ? "ring-2 ring-[var(--accent-primary)]" : "ring-0",
1515
].join(" ")}
16+
style={{
17+
borderColor: "var(--widget-border)",
18+
backgroundColor: "var(--bg-elevated)",
19+
color: "var(--text-primary)",
20+
}}
1621
>
1722
{definition.maxInputs > 0 && data.type !== "join" && (
1823
<Handle
1924
id="input"
2025
type="target"
2126
position={Position.Left}
22-
className="h-3 w-3 border-2 border-border bg-background"
27+
className="h-3 w-3 border-2"
28+
style={{ borderColor: "var(--bg-base)", backgroundColor: "var(--accent-primary)" }}
2329
/>
2430
)}
2531
{data.type === "join" && (
@@ -28,15 +34,23 @@ function StepNodeComponent({ id, data, selected }: NodeProps<BuilderNode>) {
2834
id="left"
2935
type="target"
3036
position={Position.Left}
31-
style={{ top: "34%" }}
32-
className="h-3 w-3 border-2 border-border bg-background"
37+
className="h-3 w-3 border-2"
38+
style={{
39+
top: "34%",
40+
borderColor: "var(--bg-base)",
41+
backgroundColor: "var(--accent-primary)",
42+
}}
3343
/>
3444
<Handle
3545
id="right"
3646
type="target"
3747
position={Position.Left}
38-
style={{ top: "66%" }}
39-
className="h-3 w-3 border-2 border-border bg-background"
48+
className="h-3 w-3 border-2"
49+
style={{
50+
top: "66%",
51+
borderColor: "var(--bg-base)",
52+
backgroundColor: "var(--accent-primary)",
53+
}}
4054
/>
4155
</>
4256
)}
@@ -45,17 +59,25 @@ function StepNodeComponent({ id, data, selected }: NodeProps<BuilderNode>) {
4559
id="output"
4660
type="source"
4761
position={Position.Right}
48-
className="h-3 w-3 border-2 border-border bg-background"
62+
className="h-3 w-3 border-2"
63+
style={{ borderColor: "var(--bg-base)", backgroundColor: "var(--accent-secondary)" }}
4964
/>
5065
)}
5166

5267
<div className="flex items-start justify-between gap-2">
5368
<div className="space-y-0.5">
5469
<p className="text-sm font-semibold leading-none">{data.label}</p>
55-
<p className="text-xs text-muted-foreground">{definition.label}</p>
70+
<p className="text-xs text-[var(--text-secondary)]">{definition.label}</p>
5671
</div>
5772
{!data.backendSupported && (
58-
<span className="rounded border border-amber-500/40 bg-amber-500/10 px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-amber-700">
73+
<span
74+
className="rounded border px-1.5 py-0.5 text-[10px] uppercase tracking-wide"
75+
style={{
76+
borderColor: "color-mix(in srgb, var(--accent-warning) 45%, transparent)",
77+
color: "var(--accent-warning)",
78+
backgroundColor: "color-mix(in srgb, var(--accent-warning) 14%, transparent)",
79+
}}
80+
>
5981
Visual only
6082
</span>
6183
)}
@@ -67,7 +89,8 @@ function StepNodeComponent({ id, data, selected }: NodeProps<BuilderNode>) {
6789
onClick={() => data.onConfigure?.(id)}
6890
data-testid={`config-btn-${id}`}
6991
aria-label={`Configure ${data.label}`}
70-
className="rounded border border-border px-2 py-1 text-[11px] hover:bg-muted"
92+
className="rounded border px-2 py-1 text-[11px] transition-colors hover:bg-[var(--interactive-hover)]"
93+
style={{ borderColor: "var(--widget-border)" }}
7194
>
7295
7396
</button>
@@ -76,7 +99,8 @@ function StepNodeComponent({ id, data, selected }: NodeProps<BuilderNode>) {
7699
onClick={() => data.onDelete?.(id)}
77100
data-testid={`delete-btn-${id}`}
78101
aria-label={`Delete ${data.label}`}
79-
className="rounded border border-destructive/30 px-2 py-1 text-[11px] text-destructive hover:bg-destructive/10"
102+
className="rounded border px-2 py-1 text-[11px] text-[var(--accent-error)] transition-colors hover:bg-[var(--interactive-hover)]"
103+
style={{ borderColor: "color-mix(in srgb, var(--accent-error) 45%, transparent)" }}
80104
>
81105
×
82106
</button>

frontend/components/pipeline-builder/StepPalette.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ export function StepPalette({ onDragStart, onAddStep }: StepPaletteProps) {
2323
};
2424

2525
return (
26-
<aside className="w-72 shrink-0 border-r bg-card/40 p-3" data-testid="step-palette">
27-
<h3 className="mb-3 text-sm font-semibold">Step Palette</h3>
26+
<aside
27+
className="flex h-full w-64 shrink-0 flex-col border-r bg-[var(--bg-surface)] p-3 text-[var(--text-primary)]"
28+
style={{ borderColor: "var(--widget-border)" }}
29+
data-testid="step-palette"
30+
>
31+
<h3 className="mb-3 text-xs font-semibold uppercase tracking-wider text-[var(--text-secondary)]">Step Palette</h3>
2832

29-
<div className="space-y-4">
33+
<div className="flex-1 space-y-4 overflow-y-auto pr-1">
3034
{CATEGORY_ORDER.map((category) => {
3135
const categorySteps = STEP_TYPES.filter(
3236
(stepType) => STEP_DEFINITIONS[stepType].category === category,
@@ -37,7 +41,7 @@ export function StepPalette({ onDragStart, onAddStep }: StepPaletteProps) {
3741

3842
return (
3943
<section key={category} className="space-y-2">
40-
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
44+
<p className="text-xs font-medium uppercase tracking-wide text-[var(--text-secondary)]">
4145
{STEP_CATEGORY_LABELS[category]}
4246
</p>
4347

@@ -55,18 +59,26 @@ export function StepPalette({ onDragStart, onAddStep }: StepPaletteProps) {
5559
}}
5660
onClick={() => handleCardClick(stepType)}
5761
data-testid={`step-card-${stepType}`}
58-
className="w-full rounded-md border bg-background px-2.5 py-2 text-left hover:bg-muted"
62+
className="w-full rounded-md border bg-[var(--bg-elevated)] px-2.5 py-2 text-left transition-colors hover:bg-[var(--interactive-hover)]"
63+
style={{ borderColor: "var(--widget-border)" }}
5964
>
6065
<div className="flex items-start justify-between gap-2">
6166
<div className="space-y-0.5">
62-
<p className="text-sm font-medium leading-none">
67+
<p className="text-sm font-medium leading-none text-[var(--text-primary)]">
6368
<span className="mr-1.5">{definition.icon}</span>
6469
{definition.label}
6570
</p>
66-
<p className="text-[11px] text-muted-foreground">{definition.description}</p>
71+
<p className="text-[11px] text-[var(--text-secondary)]">{definition.description}</p>
6772
</div>
6873
{!definition.backendSupported && (
69-
<span className="rounded border border-[var(--widget-border)] bg-[var(--bg-surface)] px-1 py-0.5 text-[10px] uppercase text-[var(--text-primary)]">
74+
<span
75+
className="rounded border px-1 py-0.5 text-[10px] font-semibold uppercase"
76+
style={{
77+
borderColor: "color-mix(in srgb, var(--accent-warning) 45%, transparent)",
78+
color: "var(--accent-warning)",
79+
backgroundColor: "color-mix(in srgb, var(--accent-warning) 14%, transparent)",
80+
}}
81+
>
7082
Visual
7183
</span>
7284
)}

0 commit comments

Comments
 (0)