diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2921d77a..b2d815f5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,6 +18,8 @@ "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@xterm/addon-fit": "^0.11.0", + "@xterm/addon-search": "^0.16.0", + "@xterm/addon-serialize": "^0.14.0", "@xterm/xterm": "^6.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -28,7 +30,9 @@ "react-dom": "^19.2.0", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "xterm-addon-search": "^0.13.0", + "xterm-addon-serialize": "^0.11.0" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -2964,6 +2968,18 @@ "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==", "license": "MIT" }, + "node_modules/@xterm/addon-search": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0.tgz", + "integrity": "sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==", + "license": "MIT" + }, + "node_modules/@xterm/addon-serialize": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0.tgz", + "integrity": "sha512-uteyTU1EkrQa2Ux6P/uFl2fzmXI46jy5uoQMKEOM0fKTyiW7cSn0WrFenHm5vO5uEXX/GpwW/FgILvv3r0WbkA==", + "license": "MIT" + }, "node_modules/@xterm/xterm": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", @@ -5050,6 +5066,34 @@ "node": ">=0.10.0" } }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", + "license": "MIT", + "peer": true + }, + "node_modules/xterm-addon-search": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/xterm-addon-search/-/xterm-addon-search-0.13.0.tgz", + "integrity": "sha512-sDUwG4CnqxUjSEFh676DlS3gsh3XYCzAvBPSvJ5OPgF3MRL3iHLPfsb06doRicLC2xXNpeG2cWk8x1qpESWJMA==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-search instead.", + "license": "MIT", + "peerDependencies": { + "xterm": "^5.0.0" + } + }, + "node_modules/xterm-addon-serialize": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0.tgz", + "integrity": "sha512-2CNDnmLdLkNWfsxNFkGsI5FE9W/BbsMzeOrbu59yNqH9L6k1gmL+Ab6VXxEp2NQUJSzaiqi6t0nFR5k5EDkVIg==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-serialize instead.", + "license": "MIT", + "peerDependencies": { + "xterm": "^5.0.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 75297d01..eb91a28f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,8 @@ "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@xterm/addon-fit": "^0.11.0", + "@xterm/addon-search": "^0.16.0", + "@xterm/addon-serialize": "^0.14.0", "@xterm/xterm": "^6.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -30,7 +32,9 @@ "react-dom": "^19.2.0", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "xterm-addon-search": "^0.13.0", + "xterm-addon-serialize": "^0.11.0" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/frontend/src/components/EditorLayout.tsx b/frontend/src/components/EditorLayout.tsx index 2289c551..debef327 100644 --- a/frontend/src/components/EditorLayout.tsx +++ b/frontend/src/components/EditorLayout.tsx @@ -26,6 +26,7 @@ interface ContainerInfo { Id: string; Names: string[]; State: string; + Status?: string; Ports?: { PrivatePort: number, PublicPort: number }[]; } @@ -445,6 +446,19 @@ export default function EditorLayout() { return stackName; }; + const getContainerBadge = (container: ContainerInfo) => { + const status = (container.Status || '').toLowerCase(); + const state = (container.State || '').toLowerCase(); + + if (status.includes('(unhealthy)') || state === 'exited' || state === 'dead') { + return { variant: 'destructive' as const, text: container.State }; + } + if (status.includes('(starting)')) { + return { variant: 'secondary' as const, text: container.State }; + } + return { variant: 'default' as const, text: container.State }; + }; + return (