{selectedInFilter.length} selected
Clear
{#if selectedStopped.length > 0 && $canAccess('containers', 'start')}
confirmBulkStart = open}
>
{#snippet children({ open })}
Start
{/snippet}
{/if}
{#if selectedRunning.length > 0 && $canAccess('containers', 'stop')}
confirmBulkStop = open}
>
{#snippet children({ open })}
Stop
{/snippet}
confirmBulkPause = open}
>
{#snippet children({ open })}
Pause
{/snippet}
{/if}
{#if selectedPaused.length > 0 && $canAccess('containers', 'start')}
confirmBulkUnpause = open}
>
{#snippet children({ open })}
Unpause
{/snippet}
{/if}
{#if $canAccess('containers', 'restart')}
confirmBulkRestart = open}
>
{#snippet children({ open })}
Restart
{/snippet}
{/if}
{#if $canAccess('containers', 'remove')}
confirmBulkRemove = open}
>
{#snippet children({ open })}
Remove
{/snippet}
{/if}
{#if selectedHaveUpdates}
Update {selectedWithUpdatesCount}
{/if}
{#if bulkActionInProgress}
{/if}
{ sortField = state.field as SortField; sortDirection = state.direction; }}
highlightedKey={highlightedRowId}
rowClass={(container) => {
let classes = '';
if (currentLogsContainerId === container.id) classes += 'bg-blue-500/10 hover:bg-blue-500/15 ';
if (currentTerminalContainerId === container.id) classes += 'bg-green-500/10 hover:bg-green-500/15 ';
if ($appSettings.highlightUpdates && containersWithUpdatesSet.has(container.id)) classes += 'has-update ';
return classes;
}}
onRowClick={(container, e) => {
if (activeLogs.length > 0 || activeTerminals.length > 0) {
selectContainer(container);
}
highlightedRowId = highlightedRowId === container.id ? null : container.id;
}}
>
{#snippet cell(column, container, rowState)}
{@const ports = formatPorts(container.ports)}
{@const stack = getComposeProject(container.labels)}
{#if column.id === 'name'}
{container.name}
{:else if column.id === 'image'}
{#if containersWithUpdatesSet.has(container.id)}
{/if}
{container.image}
{:else if column.id === 'state'}
{@const StateIcon = getStatusIcon(container.state)}
{container.state}
{:else if column.id === 'health'}
{#if container.health}
{#if container.health === 'healthy'}
{:else if container.health === 'unhealthy'}
{:else}
{/if}
{:else}
-
{/if}
{:else if column.id === 'uptime'}
{formatUptime(container.status)}
{:else if column.id === 'restartCount'}
{#if container.restartCount > 0}
{container.restartCount}
{:else}
-
{/if}
{:else if column.id === 'cpu'}
{#if containerStats.get(container.id)}
{@const stats = containerStats.get(container.id)}
{stats.cpuPercent.toFixed(1)}%
{:else if container.state === 'running'}
...
{:else}
-
{/if}
{:else if column.id === 'memory'}
{#if containerStats.get(container.id)}
{@const stats = containerStats.get(container.id)}
{formatBytes(stats.memoryUsage)}
{:else if container.state === 'running'}
...
{:else}
-
{/if}
{:else if column.id === 'networkIO'}
{#if containerStats.get(container.id)}
{@const stats = containerStats.get(container.id)}
↓ {formatBytes(stats.networkRx, 0)} ↑ {formatBytes(stats.networkTx, 0)}
{:else if container.state === 'running'}
...
{:else}
-
{/if}
{:else if column.id === 'diskIO'}
{#if containerStats.get(container.id)}
{@const stats = containerStats.get(container.id)}
r {formatBytes(stats.blockRead, 0)} w {formatBytes(stats.blockWrite, 0)}
{:else if container.state === 'running'}
...
{:else}
-
{/if}
{:else if column.id === 'ip'}
{getContainerIp(container.networks)}
{:else if column.id === 'ports'}
{#if ports.length > 0}
{:else}
-
{/if}
{:else if column.id === 'autoUpdate'}
{#if autoUpdateSettings.get(container.name)?.enabled}
{@const settings = autoUpdateSettings.get(container.name)}
{settings?.label}
{#if envHasScanning}
{@const criteria = settings?.vulnerabilityCriteria || 'never'}
{@const icon = vulnerabilityCriteriaIcons[criteria]}
{#if icon}
{@const IconComponent = icon.component}
{/if}
{/if}
{:else}
-
{/if}
{:else if column.id === 'stack'}
{#if stack}
{stack}
{:else}
-
{/if}
{:else if column.id === 'actions'}
{#if containersWithUpdatesSet.has(container.id)}
updateSingleContainer(container.id, container.name)}
title="Update available - click to update"
class="p-0.5 rounded hover:bg-muted transition-colors cursor-pointer"
>
{/if}
{#if container.state === 'running' || container.state === 'restarting'}
{#if $canAccess('containers', 'stop')}
stopContainer(container.id)}
onOpenChange={(open) => confirmStopId = open ? container.id : null}
>
{#snippet children({ open })}
{/snippet}
{#if container.state === 'running'}
pauseContainer(container.id)}
title="Pause"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{/if}
{/if}
{:else if container.state === 'paused'}
{#if $canAccess('containers', 'start')}
unpauseContainer(container.id)}
title="Unpause"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{/if}
{:else}
{#if $canAccess('containers', 'start')}
startContainer(container.id)}
title="Start"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{/if}
{/if}
{#if $canAccess('containers', 'restart')}
restartContainer(container.id)}
onOpenChange={(open) => confirmRestartId = open ? container.id : null}
>
{#snippet children({ open })}
{/snippet}
{/if}
inspectContainer(container)}
title="View details"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{#if container.state === 'running' && $canAccess('containers', 'exec')}
browseFiles(container)}
title="Browse files"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{/if}
{#if $canAccess('containers', 'create')}
editContainer(container.id)}
title="Edit"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{/if}
{#if $canAccess('containers', 'logs')}
{#if hasActiveLogs(container.id)}
{ e.stopPropagation(); currentLogsContainerId = container.id; }}
title="Show logs"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{:else}
{ e.stopPropagation(); showLogs(container); }}
title="Open logs"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{/if}
{/if}
{#if container.state === 'running' && $canAccess('containers', 'exec')}
{#if hasActiveTerminal(container.id)}
{ e.stopPropagation(); currentTerminalContainerId = container.id; }}
title="Show terminal"
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
{:else}
{ terminalPopoverStates[container.id] = open; }}>
e.stopPropagation()}
class="p-0.5 rounded hover:bg-muted transition-colors opacity-70 hover:opacity-100 cursor-pointer"
>
Shell
{shellOptions.find(o => o.value === terminalShell)?.label || 'Select'}
{#each shellOptions as option}
{option.label}
{/each}
User
{userOptions.find(o => o.value === terminalUser)?.label || 'Select'}
{#each userOptions as option}
{option.label}
{/each}
startTerminal(container)}>
Connect
{/if}
{/if}
{#if $canAccess('containers', 'remove')}
removeContainer(container.id)}
onOpenChange={(open) => confirmDeleteId = open ? container.id : null}
>
{#snippet children({ open })}
{/snippet}
{/if}
{#if operationError?.id === container.id}
{operationError.message}
operationError = null} class="flex-shrink-0 hover:bg-white/20 rounded p-0.5">
{/if}
{/if}
{/snippet}
{#if layoutMode === 'vertical' && (currentLogsContainerId || currentTerminalContainerId)}
{#if currentLogsContainerId}
{@const activeLog = activeLogs.find(l => l.containerId === currentLogsContainerId)}
{#if activeLog}
closeLogs(activeLog.containerId)}
/>
{/if}
{/if}
{#if currentTerminalContainerId}
{@const activeTerminal = activeTerminals.find(t => t.containerId === currentTerminalContainerId)}
{#if activeTerminal}
closeTerminal(activeTerminal.containerId)}
/>
{/if}
{/if}
{/if}
{#if layoutMode === 'horizontal'}
{#if currentLogsContainerId}
{@const activeLog = activeLogs.find(l => l.containerId === currentLogsContainerId)}
{#if activeLog}