From 1a34f73ae3209b780558aa49c43b3405cf415467 Mon Sep 17 00:00:00 2001 From: jarek Date: Sun, 19 Apr 2026 10:28:47 +0200 Subject: [PATCH] port ranges also in stacks display --- src/lib/utils/port-format.ts | 15 +++++++++------ src/routes/stacks/+page.svelte | 13 +++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/lib/utils/port-format.ts b/src/lib/utils/port-format.ts index 1836c38..0d13444 100644 --- a/src/lib/utils/port-format.ts +++ b/src/lib/utils/port-format.ts @@ -6,23 +6,26 @@ export interface PortMapping { } interface PortInfo { - PublicPort: number; - PrivatePort: number; + PublicPort?: number; + PrivatePort?: number; + publicPort?: number; + privatePort?: number; } /** * Format Docker port mappings, collapsing consecutive ranges. + * Accepts both Docker API format (PublicPort/PrivatePort) and camelCase (publicPort/privatePort). * e.g. 8080:8080, 8081:8081, 8082:8082 → 8080-8082:8080-8082 */ export function formatPorts(ports: PortInfo[] | undefined | null): PortMapping[] { if (!ports || ports.length === 0) return []; const seen = new Set(); const individual = ports - .filter(p => p.PublicPort) + .filter(p => (p.PublicPort || p.publicPort)) .map(p => ({ - publicPort: p.PublicPort, - privatePort: p.PrivatePort, - display: `${p.PublicPort}:${p.PrivatePort}` + publicPort: p.PublicPort || p.publicPort!, + privatePort: p.PrivatePort || p.privatePort!, + display: `${p.PublicPort || p.publicPort}:${p.PrivatePort || p.privatePort}` })) .filter(p => { const key = p.display; diff --git a/src/routes/stacks/+page.svelte b/src/routes/stacks/+page.svelte index 0c8869b..c3ec61b 100644 --- a/src/routes/stacks/+page.svelte +++ b/src/routes/stacks/+page.svelte @@ -15,6 +15,7 @@ import * as Popover from '$lib/components/ui/popover'; import MultiSelectFilter from '$lib/components/MultiSelectFilter.svelte'; import { Play, Square, Trash2, Plus, ArrowBigDown, Search, Pencil, ExternalLink, GitBranch, RefreshCw, Loader2, FileCode, FileText, FileOutput, Box, RotateCcw, ScrollText, Terminal, Eye, Network, HardDrive, Heart, HeartPulse, HeartOff, ChevronsUpDown, ChevronsDownUp, Rocket, AlertTriangle, X, Layers, Pause, CircleDashed, Skull, FolderOpen, Variable, Clock, RotateCw, Import, Ship, Cable, LayoutPanelLeft, Rows3, GripVertical } from 'lucide-svelte'; + import { formatPorts } from '$lib/utils/port-format'; import ConfirmPopover from '$lib/components/ConfirmPopover.svelte'; import BatchOperationModal from '$lib/components/BatchOperationModal.svelte'; import type { ComposeStackInfo, ContainerStats } from '$lib/types'; @@ -2014,11 +2015,11 @@ {/key} {/if}
- + {#if container.ports.length > 0} - {@const uniquePorts = container.ports.filter((p, i, arr) => p.publicPort && arr.findIndex(x => x.publicPort === p.publicPort) === i)} - {#each uniquePorts as port} - {@const url = getPortUrl(port.publicPort)} + {@const mappedPorts = formatPorts(container.ports)} + {#each mappedPorts as port} + {@const url = !port.isRange ? getPortUrl(port.publicPort) : null} {#if url} - :{port.publicPort} + {port.display} {:else} - :{port.publicPort} + {port.display} {/if} {/each}