Canvas: Improve layout

Change-Id: Ife4f14d23eefc0ef0cb6b189446590fc42b8d797
diff --git a/apps/canvas/front/package-lock.json b/apps/canvas/front/package-lock.json
index a691f23..856057f 100644
--- a/apps/canvas/front/package-lock.json
+++ b/apps/canvas/front/package-lock.json
@@ -9,6 +9,7 @@
 			"version": "0.0.0",
 			"dependencies": {
 				"@hookform/resolvers": "^3.9.1",
+				"@microlink/react-json-view": "^1.26.1",
 				"@radix-ui/react-accordion": "^1.2.1",
 				"@radix-ui/react-checkbox": "^1.3.1",
 				"@radix-ui/react-collapsible": "^1.1.1",
@@ -291,6 +292,14 @@
 				"@babel/core": "^7.0.0-0"
 			}
 		},
+		"node_modules/@babel/runtime": {
+			"version": "7.27.1",
+			"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
+			"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
 		"node_modules/@babel/template": {
 			"version": "7.25.9",
 			"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
@@ -989,6 +998,23 @@
 				"@jridgewell/sourcemap-codec": "^1.4.14"
 			}
 		},
+		"node_modules/@microlink/react-json-view": {
+			"version": "1.26.1",
+			"resolved": "https://registry.npmjs.org/@microlink/react-json-view/-/react-json-view-1.26.1.tgz",
+			"integrity": "sha512-2H5QCYdZlJi+oN4YBiUYPPFTNh/KLCN9i9yz8NwmSkRqXSRXYtEVIRffc9L34jdopKGK/tK21SeuzXVJHQLkfQ==",
+			"dependencies": {
+				"react-base16-styling": "~0.9.0",
+				"react-lifecycles-compat": "~3.0.4",
+				"react-textarea-autosize": "~8.5.7"
+			},
+			"engines": {
+				"node": ">=17"
+			},
+			"peerDependencies": {
+				"react": ">= 15",
+				"react-dom": ">= 15"
+			}
+		},
 		"node_modules/@nodelib/fs.scandir": {
 			"version": "2.1.5",
 			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2631,6 +2657,11 @@
 				"@babel/types": "^7.20.7"
 			}
 		},
+		"node_modules/@types/base16": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz",
+			"integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A=="
+		},
 		"node_modules/@types/d3-color": {
 			"version": "3.1.3",
 			"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
@@ -2686,6 +2717,11 @@
 			"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
 			"dev": true
 		},
+		"node_modules/@types/lodash": {
+			"version": "4.17.16",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
+			"integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g=="
+		},
 		"node_modules/@types/node": {
 			"version": "22.8.4",
 			"resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.4.tgz",
@@ -3167,6 +3203,11 @@
 			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
 			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
 		},
+		"node_modules/base16": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
+			"integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ=="
+		},
 		"node_modules/binary-extensions": {
 			"version": "2.3.0",
 			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -3350,6 +3391,15 @@
 				"node": ">=6"
 			}
 		},
+		"node_modules/color": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+			"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+			"dependencies": {
+				"color-convert": "^1.9.3",
+				"color-string": "^1.6.0"
+			}
+		},
 		"node_modules/color-convert": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -3366,6 +3416,28 @@
 			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
 			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
 		},
+		"node_modules/color-string": {
+			"version": "1.9.1",
+			"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+			"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+			"dependencies": {
+				"color-name": "^1.0.0",
+				"simple-swizzle": "^0.2.2"
+			}
+		},
+		"node_modules/color/node_modules/color-convert": {
+			"version": "1.9.3",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+			"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+			"dependencies": {
+				"color-name": "1.1.3"
+			}
+		},
+		"node_modules/color/node_modules/color-name": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+			"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+		},
 		"node_modules/commander": {
 			"version": "4.1.1",
 			"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -3413,8 +3485,7 @@
 		"node_modules/csstype": {
 			"version": "3.1.3",
 			"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
-			"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
-			"devOptional": true
+			"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
 		},
 		"node_modules/d3-color": {
 			"version": "3.1.0",
@@ -4110,6 +4181,11 @@
 				"loose-envify": "^1.0.0"
 			}
 		},
+		"node_modules/is-arrayish": {
+			"version": "0.3.2",
+			"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+			"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+		},
 		"node_modules/is-binary-path": {
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -4306,6 +4382,11 @@
 				"url": "https://github.com/sponsors/sindresorhus"
 			}
 		},
+		"node_modules/lodash.curry": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
+			"integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA=="
+		},
 		"node_modules/lodash.merge": {
 			"version": "4.6.2",
 			"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -4821,6 +4902,20 @@
 				"node": ">=0.10.0"
 			}
 		},
+		"node_modules/react-base16-styling": {
+			"version": "0.9.1",
+			"resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz",
+			"integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==",
+			"dependencies": {
+				"@babel/runtime": "^7.16.7",
+				"@types/base16": "^1.0.2",
+				"@types/lodash": "^4.14.178",
+				"base16": "^1.0.0",
+				"color": "^3.2.1",
+				"csstype": "^3.0.10",
+				"lodash.curry": "^4.1.1"
+			}
+		},
 		"node_modules/react-dom": {
 			"version": "18.3.1",
 			"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@@ -4856,6 +4951,11 @@
 				"react": "*"
 			}
 		},
+		"node_modules/react-lifecycles-compat": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+			"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+		},
 		"node_modules/react-refresh": {
 			"version": "0.14.2",
 			"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -4941,6 +5041,22 @@
 				}
 			}
 		},
+		"node_modules/react-textarea-autosize": {
+			"version": "8.5.9",
+			"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz",
+			"integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==",
+			"dependencies": {
+				"@babel/runtime": "^7.20.13",
+				"use-composed-ref": "^1.3.0",
+				"use-latest": "^1.2.1"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"peerDependencies": {
+				"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+			}
+		},
 		"node_modules/read-cache": {
 			"version": "1.0.0",
 			"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -5100,6 +5216,14 @@
 				"url": "https://github.com/sponsors/isaacs"
 			}
 		},
+		"node_modules/simple-swizzle": {
+			"version": "0.2.2",
+			"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+			"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+			"dependencies": {
+				"is-arrayish": "^0.3.1"
+			}
+		},
 		"node_modules/source-map-js": {
 			"version": "1.2.1",
 			"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5476,6 +5600,48 @@
 				}
 			}
 		},
+		"node_modules/use-composed-ref": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz",
+			"integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==",
+			"peerDependencies": {
+				"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+			},
+			"peerDependenciesMeta": {
+				"@types/react": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/use-isomorphic-layout-effect": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
+			"integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==",
+			"peerDependencies": {
+				"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+			},
+			"peerDependenciesMeta": {
+				"@types/react": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/use-latest": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz",
+			"integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==",
+			"dependencies": {
+				"use-isomorphic-layout-effect": "^1.1.1"
+			},
+			"peerDependencies": {
+				"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+			},
+			"peerDependenciesMeta": {
+				"@types/react": {
+					"optional": true
+				}
+			}
+		},
 		"node_modules/use-sidecar": {
 			"version": "1.1.2",
 			"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
diff --git a/apps/canvas/front/package.json b/apps/canvas/front/package.json
index 646ef6b..c99ec28 100644
--- a/apps/canvas/front/package.json
+++ b/apps/canvas/front/package.json
@@ -17,6 +17,7 @@
 	},
 	"dependencies": {
 		"@hookform/resolvers": "^3.9.1",
+		"@microlink/react-json-view": "^1.26.1",
 		"@radix-ui/react-accordion": "^1.2.1",
 		"@radix-ui/react-checkbox": "^1.3.1",
 		"@radix-ui/react-collapsible": "^1.1.1",
@@ -75,4 +76,4 @@
 		"bracketSpacing": true,
 		"arrowParens": "always"
 	}
-}
\ No newline at end of file
+}
diff --git a/apps/canvas/front/src/App.tsx b/apps/canvas/front/src/App.tsx
index afacdb1..f37370a 100644
--- a/apps/canvas/front/src/App.tsx
+++ b/apps/canvas/front/src/App.tsx
@@ -5,38 +5,42 @@
 import { Config } from "./Config";
 import { Integrations } from "./Integrations";
 import { Toaster } from "./components/ui/toaster";
-import { Header } from "./Header";
+import { ProjectSelect } from "./ProjectSelect";
 import { Logs } from "./components/logs";
 
 export default function App() {
 	return (
 		<ReactFlowProvider>
-			<Header />
-			<AppImpl />
-			<Toaster />
+			<div className="h-screen flex flex-col">
+				<AppImpl />
+				<Toaster />
+			</div>
 		</ReactFlowProvider>
 	);
 }
 
 function AppImpl() {
 	return (
-		<Tabs defaultValue="canvas">
-			<TabsList>
-				<TabsTrigger value="canvas">Canvas</TabsTrigger>
-				<TabsTrigger value="config">Config</TabsTrigger>
-				<TabsTrigger value="integrations">Integrations</TabsTrigger>
-				<TabsTrigger value="logs">Logs</TabsTrigger>
-			</TabsList>
-			<TabsContent value="canvas">
+		<Tabs defaultValue="canvas" className="flex-1 flex flex-col min-h-0">
+			<div className="flex items-center justify-between px-4 border-b">
+				<TabsList>
+					<TabsTrigger value="canvas">Canvas</TabsTrigger>
+					<TabsTrigger value="logs">Logs</TabsTrigger>
+					<TabsTrigger value="config">Config</TabsTrigger>
+					<TabsTrigger value="integrations">Integrations</TabsTrigger>
+				</TabsList>
+				<ProjectSelect />
+			</div>
+			<TabsContent value="canvas" className="flex-1 min-h-0">
 				<CanvasBuilder />
 			</TabsContent>
-			<TabsContent value="config">
+			<TabsContent value="config" className="flex-1 min-h-0">
 				<Config />
 			</TabsContent>
-			<TabsContent value="integrations">
+			<TabsContent value="integrations" className="flex-1 min-h-0">
 				<Integrations />
 			</TabsContent>
-			<TabsContent value="logs">
+			<TabsContent value="logs" className="flex-1 min-h-0">
 				<Logs />
 			</TabsContent>
 		</Tabs>
diff --git a/apps/canvas/front/src/Config.tsx b/apps/canvas/front/src/Config.tsx
index df03746..7a93634 100644
--- a/apps/canvas/front/src/Config.tsx
+++ b/apps/canvas/front/src/Config.tsx
@@ -2,6 +2,8 @@
 import { AppNode, useEnv, useProjectId } from "./lib/state";
 import { generateDodoConfig } from "./lib/config";
 import { useEffect, useMemo, useState } from "react";
+import { Card, CardContent } from "./components/ui/card";
+import JSONView from "@microlink/react-json-view";
 
 export function Config() {
 	const env = useEnv();
@@ -14,11 +16,21 @@
 			setNodes(n);
 		}
 	}, [n, setNodes]);
-	const config = useMemo(() => generateDodoConfig(projectId, nodes, env), [projectId, nodes, env]);
-	const configS = useMemo(() => JSON.stringify(config, undefined, 4), [config]);
+	const config = useMemo(() => generateDodoConfig(projectId, nodes, env) || {}, [projectId, nodes, env]);
 	return (
-		<div className="px-5">
-			<pre>{configS}</pre>
-		</div>
+		<Card className="h-full flex flex-col">
+			<CardContent className="flex-1 min-h-0 p-4">
+				<div className="h-full p-4 bg-muted rounded-lg overflow-auto">
+					<JSONView
+						src={config as object}
+						theme="rjv-default"
+						name={false}
+						displayDataTypes={false}
+						enableClipboard={true}
+						style={{ fontFamily: "JetBrains Mono" }}
+					/>
+				</div>
+			</CardContent>
+		</Card>
 	);
 }
diff --git a/apps/canvas/front/src/Header.tsx b/apps/canvas/front/src/ProjectSelect.tsx
similarity index 78%
rename from apps/canvas/front/src/Header.tsx
rename to apps/canvas/front/src/ProjectSelect.tsx
index 979d85c..5178b5e 100644
--- a/apps/canvas/front/src/Header.tsx
+++ b/apps/canvas/front/src/ProjectSelect.tsx
@@ -7,7 +7,7 @@
 import { Dialog, DialogContent, DialogTrigger } from "./components/ui/dialog";
 import { useToast } from "@/hooks/use-toast";
 
-export function Header() {
+export function ProjectSelect() {
 	const { toast } = useToast();
 	const store = useStateStore();
 	const [projects, setProjects] = useState<Project[]>([]);
@@ -96,26 +96,26 @@
 			});
 	}, [name, setCreateNewOpen, toast, store, refreshProjects]);
 	return (
-		<div className="flex flex-row h-9">
-			<Select onValueChange={onSelect} value={project}>
-				<SelectTrigger>
-					<SelectValue placeholder="Choose Project" defaultValue={project} />
-				</SelectTrigger>
-				<SelectContent>
-					{projects.map((p) => (
-						<SelectItem value={p.id}>{p.name}</SelectItem>
-					))}
-					<SelectItem value={"create-new"}>
-						<Dialog open={createNewOpen} onOpenChange={setCreateNewOpen}>
-							<DialogTrigger>Create New</DialogTrigger>
-							<DialogContent>
-								<Input type="text" placeholder="Name" onChange={updateName} />
-								<Button onClick={createNew}>Create New</Button>
-							</DialogContent>
-						</Dialog>
+		<Select onValueChange={onSelect} value={project}>
+			<SelectTrigger className="w-[200px]">
+				<SelectValue placeholder="Choose Project" defaultValue={project} />
+			</SelectTrigger>
+			<SelectContent>
+				<SelectItem value={"create-new"}>
+					<Dialog open={createNewOpen} onOpenChange={setCreateNewOpen}>
+						<DialogTrigger>Create New</DialogTrigger>
+						<DialogContent>
+							<Input type="text" placeholder="Name" onChange={updateName} />
+							<Button onClick={createNew}>Create New</Button>
+						</DialogContent>
+					</Dialog>
+				</SelectItem>
+				{projects.map((p) => (
+					<SelectItem key={p.id} value={p.id}>
+						{p.name}
 					</SelectItem>
-				</SelectContent>
-			</Select>
-		</div>
+				))}
+			</SelectContent>
+		</Select>
 	);
 }
diff --git a/apps/canvas/front/src/components/details.tsx b/apps/canvas/front/src/components/details.tsx
index 252952d..cb7d7fe 100644
--- a/apps/canvas/front/src/components/details.tsx
+++ b/apps/canvas/front/src/components/details.tsx
@@ -5,6 +5,7 @@
 import { AccordionItem } from "@radix-ui/react-accordion";
 import { useMemo, useState } from "react";
 import { Icon } from "./icon";
+import { Separator } from "./ui/separator";
 
 function unique<T>(v: T, i: number, a: T[]) {
 	return a.indexOf(v) === i;
@@ -42,18 +43,21 @@
 	const all = useMemo(() => open.concat(selected).filter(unique), [open, selected]);
 	return (
 		<Accordion type="multiple" value={all} onValueChange={(v) => setOpen(v)}>
-			{sorted.map((n) => (
-				<AccordionItem key={n.id} value={n.id} className="px-3">
-					<AccordionTrigger>
-						<div className="flex flex-row space-x-2">
-							{Icon(n.type)}
-							<span>{nodeLabel(n)}</span>
-						</div>
-					</AccordionTrigger>
-					<AccordionContent>
-						<NodeDetails {...n} />
-					</AccordionContent>
-				</AccordionItem>
+			{sorted.map((n, index) => (
+				<>
+					{index > 0 && <Separator className="my-2" />}
+					<AccordionItem key={n.id} value={n.id} className="px-3">
+						<AccordionTrigger>
+							<div className="flex flex-row space-x-2">
+								{Icon(n.type)}
+								<span>{nodeLabel(n)}</span>
+							</div>
+						</AccordionTrigger>
+						<AccordionContent>
+							<NodeDetails {...n} />
+						</AccordionContent>
+					</AccordionItem>
+				</>
 			))}
 		</Accordion>
 	);
diff --git a/apps/canvas/front/src/components/logs.tsx b/apps/canvas/front/src/components/logs.tsx
index 5918cf7..a2bf1ba 100644
--- a/apps/canvas/front/src/components/logs.tsx
+++ b/apps/canvas/front/src/components/logs.tsx
@@ -5,6 +5,7 @@
 import { useToast } from "@/hooks/use-toast";
 
 // ANSI escape sequence regex
+// eslint-disable-next-line no-control-regex
 const ANSI_ESCAPE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
 
 function cleanAnsiEscapeSequences(text: string): string {
@@ -98,7 +99,7 @@
 	}, [sortedServices, selectedService]);
 
 	return (
-		<Card>
+		<Card className="h-full flex flex-col">
 			<CardHeader>
 				<Select value={selectedService} onValueChange={setSelectedService}>
 					<SelectTrigger>
@@ -113,11 +114,11 @@
 					</SelectContent>
 				</Select>
 			</CardHeader>
-			<CardContent className="h-full">
+			<CardContent className="flex-1 min-h-0">
 				{selectedService && (
 					<pre
 						ref={preRef}
-						className="p-4 bg-muted rounded-lg overflow-auto max-h-[500px] font-['JetBrains_Mono'] whitespace-pre-wrap break-all"
+						className="h-full p-4 bg-muted rounded-lg overflow-auto font-['JetBrains_Mono'] whitespace-pre-wrap break-all"
 					>
 						{cleanAnsiEscapeSequences(logs) || "No logs available"}
 					</pre>