mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-19 14:56:27 +00:00
feat(resources): add image details sheet with layer history (#925)
Adds a read-only inspect panel for Docker images. Click the eye icon on any image row to open a sheet showing: - Overview: ID (with copy), size, created date, arch/OS, author, tags - Config: Cmd, Entrypoint, WorkingDir, User, exposed ports, env (collapsible), labels (collapsible) - Layers: ordered history list with size, age, and build command per layer. Empty layers (metadata-only) are dimmed. Backend adds DockerController.inspectImage(id) which combines image.inspect() and image.history() in parallel, exposed via GET /api/system/images/:id. The route accepts both bare hex IDs and sha256-prefixed IDs, since the list endpoint surfaces the prefixed form. Returns 400 for malformed IDs and 404 for missing images. Documents the new panel in docs/features/resources.mdx under Images.
This commit is contained in:
@@ -37,6 +37,7 @@ import type { SenchoOpenLogsDetail } from '@/lib/events';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { ReclaimHero } from './resources/ReclaimHero';
|
||||
import { FootprintTreemap } from './resources/FootprintTreemap';
|
||||
import { ImageDetailsSheet } from './resources/ImageDetailsSheet';
|
||||
import { TabLanding, type TabLandingEntry } from './resources/TabLanding';
|
||||
|
||||
const NetworkTopologyView = lazy(() => import('./NetworkTopologyView'));
|
||||
@@ -392,6 +393,7 @@ export default function ResourcesView() {
|
||||
const [isCreatingNetwork, setIsCreatingNetwork] = useState(false);
|
||||
const [inspectNetwork, setInspectNetwork] = useState<NetworkInspectData | null>(null);
|
||||
const [inspectLoadingId, setInspectLoadingId] = useState<string | null>(null);
|
||||
const [inspectImageId, setInspectImageId] = useState<string | null>(null);
|
||||
|
||||
// Unmanaged container state
|
||||
const [selectedOrphans, setSelectedOrphans] = useState<string[]>([]);
|
||||
@@ -858,6 +860,16 @@ export default function ResourcesView() {
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-foreground transition-colors"
|
||||
onClick={() => setInspectImageId(img.Id)}
|
||||
title="Inspect image"
|
||||
aria-label={`Inspect ${img.RepoTags?.[0] || 'image'}`}
|
||||
>
|
||||
<Eye className="w-3.5 h-3.5" strokeWidth={1.5} />
|
||||
</Button>
|
||||
{trivy.available && isAdmin && img.RepoTags?.[0] && img.RepoTags[0] !== '<none>:<none>' && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -1333,6 +1345,9 @@ export default function ResourcesView() {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Image Details Sheet */}
|
||||
<ImageDetailsSheet imageId={inspectImageId} onClose={() => setInspectImageId(null)} />
|
||||
|
||||
{/* Network Inspect Sheet */}
|
||||
<Sheet open={!!inspectNetwork} onOpenChange={open => !open && setInspectNetwork(null)}>
|
||||
<SheetContent className="sm:max-w-lg">
|
||||
|
||||
Reference in New Issue
Block a user