Skip to content

Commit 253631f

Browse files
committed
feat(design): builder shell visual alignment with GlassContainer, micro-borders
1 parent aff1fee commit 253631f

7 files changed

Lines changed: 145 additions & 135 deletions

src/components/builder/builder-canvas.tsx

Lines changed: 121 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { VersionHistoryPanel } from "./version-history-panel"
4242
import { ExportImportDialog } from "./export-import-dialog"
4343
import { BuilderCommandPalette } from "./builder-command-palette"
4444
import { NodeContextMenu, PaneContextMenu } from "./builder-context-menu"
45+
import { GlassContainer } from "@/components/ui/glass-container"
4546
import { getHistoryManager } from "@/lib/history-manager"
4647
import { useToast } from "@/hooks/use-toast"
4748
import {
@@ -747,127 +748,126 @@ function BuilderCanvasInner() {
747748
/>
748749

749750
<div className="relative flex flex-1 flex-col">
750-
<div
751-
className="border-border/80 bg-card/90 absolute top-4 right-4 left-4 z-50 flex items-center gap-3 rounded-2xl border px-3 py-2 shadow-sm"
752-
data-testid="builder-toolbar"
753-
>
754-
<div className="flex min-w-0 items-center gap-3">
755-
<h1 className="truncate text-sm font-semibold tracking-tight">
756-
{workflow?.name || "Untitled Workflow"}
757-
</h1>
758-
<span className="border-border/80 bg-muted/40 text-muted-foreground rounded-full border px-2 py-0.5 text-[10px] font-medium">
759-
v{workflow?.version || 1}
760-
</span>
761-
</div>
762-
763-
<div className="border-border/80 bg-background/70 ml-auto flex max-w-full items-center gap-1.5 overflow-x-auto rounded-full border p-1">
764-
<ExportImportDialog
765-
workflowId={workflowId}
766-
onImportSuccess={(newWorkflow) => {
767-
mutate("/api/workflows")
768-
if (newWorkflow?.id) {
769-
setWorkflowId(newWorkflow.id)
770-
mutate(`/api/workflows/${newWorkflow.id}`)
771-
} else {
772-
mutate(`/api/workflows/${workflowId}`)
773-
}
774-
}}
775-
/>
776-
<div className="bg-border/80 mx-1 h-4 w-px" />
777-
<Button
778-
variant="ghost"
779-
size="icon"
780-
className="hover:bg-accent h-8 w-8 rounded-full"
781-
onClick={handleUndo}
782-
disabled={!historyStatus?.canUndo}
783-
aria-label="Undo"
784-
title="Undo"
785-
>
786-
<Undo className="h-4 w-4" />
787-
</Button>
788-
<Button
789-
variant="ghost"
790-
size="icon"
791-
className="hover:bg-accent h-8 w-8 rounded-full"
792-
onClick={handleRedo}
793-
disabled={!historyStatus?.canRedo}
794-
aria-label="Redo"
795-
title="Redo"
796-
>
797-
<Redo className="h-4 w-4" />
798-
</Button>
799-
<div className="bg-border/80 mx-1 h-4 w-px" />
800-
<Button
801-
variant="ghost"
802-
size="icon"
803-
className="hover:bg-accent h-8 w-8 rounded-full"
804-
onClick={handleAutoLayout}
805-
title="Auto Layout"
806-
aria-label="Auto Layout"
807-
>
808-
<ArrowDownUp className="h-4 w-4" />
809-
</Button>
810-
<Button
811-
variant="ghost"
812-
size="icon"
813-
className="hover:bg-accent h-8 w-8 rounded-full"
814-
onClick={handleZoomOut}
815-
aria-label="Zoom out"
816-
title="Zoom out"
817-
>
818-
<ZoomOut className="h-4 w-4" />
819-
</Button>
820-
<span
821-
className="text-muted-foreground w-10 text-center text-xs font-medium select-none"
822-
aria-live="polite"
823-
>
824-
{Math.round((viewport?.zoom ?? 1) * 100)}%
825-
</span>
826-
<Button
827-
variant="ghost"
828-
size="icon"
829-
className="hover:bg-accent h-8 w-8 rounded-full"
830-
onClick={handleZoomIn}
831-
aria-label="Zoom in"
832-
title="Zoom in"
833-
>
834-
<ZoomIn className="h-4 w-4" />
835-
</Button>
836-
<Button
837-
variant="ghost"
838-
size="icon"
839-
className="hover:bg-accent h-8 w-8 rounded-full"
840-
onClick={handleResetView}
841-
aria-label="Fit view"
842-
title="Fit view"
843-
>
844-
<Maximize2 className="h-4 w-4" />
845-
</Button>
846-
<div className="bg-border/80 mx-1 h-4 w-px" />
847-
<Button
848-
variant="ghost"
849-
size="icon"
850-
className="hover:bg-accent h-8 w-8 rounded-full"
851-
onClick={() => setShowVersionHistory(!showVersionHistory)}
852-
aria-label="Version history"
853-
title="Version history"
854-
>
855-
<History className="h-4 w-4" />
856-
</Button>
857-
<Button
858-
variant="outline"
859-
size="sm"
860-
className="ml-1 h-8 gap-2 rounded-full border-indigo-500/20 bg-indigo-500/10 text-indigo-300 hover:bg-indigo-500/20"
861-
onClick={() => setShowExecutionMonitor(!showExecutionMonitor)}
862-
>
863-
<Play className="h-3.5 w-3.5" />
864-
{showExecutionMonitor ? "Close" : "Run"}
865-
</Button>
866-
<Button size="sm" className="h-8 gap-2 rounded-full" onClick={handleSaveVersion}>
867-
<Save className="h-3.5 w-3.5" />
868-
Save
869-
</Button>
870-
</div>
751+
<div className="absolute top-4 right-4 left-4 z-50" data-testid="builder-toolbar">
752+
<GlassContainer className="flex items-center gap-3 px-3 py-2 shadow-sm">
753+
<div className="flex min-w-0 items-center gap-3">
754+
<h1 className="truncate text-sm font-semibold tracking-tight">
755+
{workflow?.name || "Untitled Workflow"}
756+
</h1>
757+
<span className="border-border/80 bg-muted/40 text-muted-foreground rounded-full border px-2 py-0.5 text-[10px] font-medium">
758+
v{workflow?.version || 1}
759+
</span>
760+
</div>
761+
762+
<div className="border-border/80 bg-background/70 ml-auto flex max-w-full items-center gap-1.5 overflow-x-auto rounded-full border p-1">
763+
<ExportImportDialog
764+
workflowId={workflowId}
765+
onImportSuccess={(newWorkflow) => {
766+
mutate("/api/workflows")
767+
if (newWorkflow?.id) {
768+
setWorkflowId(newWorkflow.id)
769+
mutate(`/api/workflows/${newWorkflow.id}`)
770+
} else {
771+
mutate(`/api/workflows/${workflowId}`)
772+
}
773+
}}
774+
/>
775+
<div className="bg-border/80 mx-1 h-4 w-px" />
776+
<Button
777+
variant="ghost"
778+
size="icon"
779+
className="hover:bg-accent h-8 w-8 rounded-full"
780+
onClick={handleUndo}
781+
disabled={!historyStatus?.canUndo}
782+
aria-label="Undo"
783+
title="Undo"
784+
>
785+
<Undo className="h-4 w-4" />
786+
</Button>
787+
<Button
788+
variant="ghost"
789+
size="icon"
790+
className="hover:bg-accent h-8 w-8 rounded-full"
791+
onClick={handleRedo}
792+
disabled={!historyStatus?.canRedo}
793+
aria-label="Redo"
794+
title="Redo"
795+
>
796+
<Redo className="h-4 w-4" />
797+
</Button>
798+
<div className="bg-border/80 mx-1 h-4 w-px" />
799+
<Button
800+
variant="ghost"
801+
size="icon"
802+
className="hover:bg-accent h-8 w-8 rounded-full"
803+
onClick={handleAutoLayout}
804+
title="Auto Layout"
805+
aria-label="Auto Layout"
806+
>
807+
<ArrowDownUp className="h-4 w-4" />
808+
</Button>
809+
<Button
810+
variant="ghost"
811+
size="icon"
812+
className="hover:bg-accent h-8 w-8 rounded-full"
813+
onClick={handleZoomOut}
814+
aria-label="Zoom out"
815+
title="Zoom out"
816+
>
817+
<ZoomOut className="h-4 w-4" />
818+
</Button>
819+
<span
820+
className="text-muted-foreground w-10 text-center text-xs font-medium select-none"
821+
aria-live="polite"
822+
>
823+
{Math.round((viewport?.zoom ?? 1) * 100)}%
824+
</span>
825+
<Button
826+
variant="ghost"
827+
size="icon"
828+
className="hover:bg-accent h-8 w-8 rounded-full"
829+
onClick={handleZoomIn}
830+
aria-label="Zoom in"
831+
title="Zoom in"
832+
>
833+
<ZoomIn className="h-4 w-4" />
834+
</Button>
835+
<Button
836+
variant="ghost"
837+
size="icon"
838+
className="hover:bg-accent h-8 w-8 rounded-full"
839+
onClick={handleResetView}
840+
aria-label="Fit view"
841+
title="Fit view"
842+
>
843+
<Maximize2 className="h-4 w-4" />
844+
</Button>
845+
<div className="bg-border/80 mx-1 h-4 w-px" />
846+
<Button
847+
variant="ghost"
848+
size="icon"
849+
className="hover:bg-accent h-8 w-8 rounded-full"
850+
onClick={() => setShowVersionHistory(!showVersionHistory)}
851+
aria-label="Version history"
852+
title="Version history"
853+
>
854+
<History className="h-4 w-4" />
855+
</Button>
856+
<Button
857+
variant="outline"
858+
size="sm"
859+
className="ml-1 h-8 gap-2 rounded-full border-indigo-500/20 bg-indigo-500/10 text-indigo-300 hover:bg-indigo-500/20"
860+
onClick={() => setShowExecutionMonitor(!showExecutionMonitor)}
861+
>
862+
<Play className="h-3.5 w-3.5" />
863+
{showExecutionMonitor ? "Close" : "Run"}
864+
</Button>
865+
<Button size="sm" className="h-8 gap-2 rounded-full" onClick={handleSaveVersion}>
866+
<Save className="h-3.5 w-3.5" />
867+
Save
868+
</Button>
869+
</div>
870+
</GlassContainer>
871871
</div>
872872

873873
<div

src/components/builder/builder-command-palette.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function BuilderCommandPalette({
6565
onOpenChange={onOpenChange}
6666
title="Command Palette"
6767
description="Search for a command to run..."
68-
className="border-border/80"
68+
className="border-border/80 bg-background/80 backdrop-blur-lg"
6969
>
7070
<CommandInput placeholder="Search commands..." />
7171
<CommandList>

src/components/builder/builder-context-menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
} from "lucide-react"
3030
import type { WorkflowNode } from "@/lib/workflow-types"
3131

32-
const MENU_CONTENT = "border border-border/80 bg-popover text-popover-foreground shadow-none"
32+
const MENU_CONTENT = "border border-white/10 bg-popover text-popover-foreground shadow-none"
3333

3434
export interface NodeContextMenuProps {
3535
onDuplicate: () => void

src/components/builder/execution-monitor.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Input } from "@/components/ui/input"
77
import { cn } from "@/lib/utils"
88
import type { WorkflowExecution, ExecutionLog } from "@/lib/workflow-types"
99
import { ScrollArea } from "@/components/ui/scroll-area"
10+
import { EmptyState } from "@/components/ui/empty-state"
1011

1112
interface ExecutionMonitorProps {
1213
workflowId: string
@@ -100,7 +101,7 @@ export function ExecutionMonitor({
100101
if (!isOpen) return null
101102

102103
return (
103-
<div className="bg-card border-border fixed inset-y-0 right-0 z-50 flex w-96 flex-col border-l shadow-xl">
104+
<div className="bg-card/95 border-border fixed inset-y-0 right-0 z-50 flex w-96 flex-col border-l shadow-xl backdrop-blur-md">
104105
{/* Header */}
105106
<div className="border-border flex h-14 items-center justify-between border-b px-4">
106107
<h2 className="font-semibold">Execution Monitor</h2>
@@ -190,8 +191,12 @@ export function ExecutionMonitor({
190191
)}
191192

192193
{!execution && !isExecuting && (
193-
<div className="text-muted-foreground flex flex-1 items-center justify-center text-sm">
194-
Enter a message and click Run to start
194+
<div className="flex flex-1 items-center justify-center p-6">
195+
<EmptyState
196+
icon={Play}
197+
title="Ready to run"
198+
description="Enter a message above and click Run to start the workflow."
199+
/>
195200
</div>
196201
)}
197202
</div>

src/components/builder/node-properties-panel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export function NodePropertiesPanel({
9797
return (
9898
<div
9999
className={cn(
100-
"border-border/80 bg-card/80 relative z-40 border-l transition-all duration-300",
100+
"border-border/80 bg-card/90 relative z-40 border-l backdrop-blur-md transition-all duration-300",
101101
isOpen ? "w-80" : "w-0",
102102
)}
103103
>
@@ -125,7 +125,7 @@ export function NodePropertiesPanel({
125125
<Icon className="text-foreground h-5 w-5" />
126126
</div>
127127
<div>
128-
<h2 className="text-foreground font-semibold tracking-tight">
128+
<h2 className="text-foreground leading-snug font-semibold tracking-tight">
129129
{node.data.label}
130130
</h2>
131131
<p className="text-muted-foreground/70 mt-0.5 text-[11px] font-medium tracking-widest uppercase">

src/components/builder/node-sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export function NodeSidebar({ isOpen, onToggle, onAddNode }: NodeSidebarProps) {
165165
return (
166166
<div
167167
className={cn(
168-
"border-border/80 bg-card/80 relative z-40 border-r transition-all duration-300",
168+
"border-border/80 bg-card/90 relative z-40 border-r backdrop-blur-md transition-all duration-300",
169169
isOpen ? "w-72" : "w-0",
170170
)}
171171
>
@@ -191,7 +191,7 @@ export function NodeSidebar({ isOpen, onToggle, onAddNode }: NodeSidebarProps) {
191191
className="flex h-full flex-col"
192192
>
193193
<div className="border-border/80 border-b p-5">
194-
<h2 className="mb-4 text-lg font-semibold tracking-tight">Add Nodes</h2>
194+
<h2 className="mb-4 text-lg leading-snug font-semibold tracking-tight">Add Nodes</h2>
195195
<div className="group relative">
196196
<Search className="text-muted-foreground group-focus-within:text-primary absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transition-colors" />
197197
<Input

src/components/builder/version-history-panel.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ScrollArea } from "@/components/ui/scroll-area"
88
import { cn } from "@/lib/utils"
99
import type { WorkflowVersion } from "@/lib/workflow-types"
1010
import useSWR from "swr"
11+
import { EmptyState } from "@/components/ui/empty-state"
1112

1213
const fetcher = (url: string) => fetch(url).then((res) => res.json())
1314

@@ -63,10 +64,10 @@ export function VersionHistoryPanel({
6364
if (!isOpen) return null
6465

6566
return (
66-
<div className="bg-card border-border fixed inset-y-0 right-0 z-40 flex w-80 flex-col border-l shadow-xl">
67+
<div className="bg-card/95 border-border fixed inset-y-0 right-0 z-40 flex w-80 flex-col border-l shadow-xl backdrop-blur-md">
6768
<div className="border-border flex h-14 items-center justify-between border-b px-4">
6869
<div className="flex items-center gap-2">
69-
<History className="h-4 w-4" />
70+
<History className="text-muted-foreground h-4 w-4" />
7071
<h2 className="font-semibold">Version History</h2>
7172
</div>
7273
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggle}>
@@ -179,9 +180,13 @@ export function VersionHistoryPanel({
179180
))}
180181

181182
{(!versions || versions.length === 0) && (
182-
<div className="text-muted-foreground py-8 text-center text-sm">
183-
No versions yet. Save your first version.
184-
</div>
183+
<EmptyState
184+
icon={History}
185+
title="No versions yet"
186+
description="Save your first version to track changes."
187+
actionLabel="Save Version"
188+
onAction={handleCreateVersion}
189+
/>
185190
)}
186191
</div>
187192
</ScrollArea>

0 commit comments

Comments
 (0)