{#if $environments.length === 0 || !$currentEnvironment}
{:else}
{#if layoutMode === 'single'}
{#if dropdownOpen}
{#if filteredContainers().length === 0}
{containers.length === 0 ? 'No containers' : 'No matches found'}
{:else} {#each filteredContainers() as container} {@const isCurrentSelection = selectedContainer?.id === container.id}
selectContainer(container)} class="w-full px-3 py-2 text-left text-sm hover:bg-muted transition-colors flex items-center gap-2 {isCurrentSelection ? 'bg-muted' : ''}" >
{container.name}
({container.image})
{#if isCurrentSelection}
current
{/if}
{/each} {/if}
{/if}
{:else if layoutMode === 'multi'}
{:else}
{/if}
{#if layoutMode === 'multi' || layoutMode === 'grouped'}
{#if layoutMode === 'grouped'}
Select all
|
Clear
{/if}
{#if layoutMode === 'grouped'} {@const validFavoriteGroups = favoriteGroups.filter(g => g?.name && g?.containers)} {#if validFavoriteGroups.length > 0}
Saved groups
{#each validFavoriteGroups as savedGroup, idx (savedGroup.name || `group-${idx}`)}
loadFavoriteGroup(savedGroup)} onkeydown={(e) => e.key === 'Enter' && loadFavoriteGroup(savedGroup)} role="button" tabindex="0" >
{savedGroup.name}
{savedGroup.containers.length} container{savedGroup.containers.length !== 1 ? 's' : ''}
{ e.stopPropagation(); deleteFavoriteGroup(savedGroup.name); }} class="p-0.5 rounded hover:bg-red-500/20 transition-colors opacity-0 [.saved-group-item:hover_&]:opacity-100" title="Delete group" >
{/each}
{/if} {/if} {#if filteredContainers().length === 0}
{containers.length === 0 ? 'No containers' : 'No matches found'}
{:else} {#if layoutMode === 'multi' && favoriteContainers().length > 0}
Favorites
{#each favoriteContainers() as container} {@const isMultiSelected = multiModeSelections.has(container.id)}
selectContainer(container)} onkeydown={(e) => e.key === 'Enter' && selectContainer(container)} role="button" tabindex="0" draggable="true" ondragstart={(e) => handleDragStart(e, container.name)} ondragover={(e) => handleDragOver(e, container.name)} ondragleave={handleDragLeave} ondragend={handleDragEnd} ondrop={(e) => handleDrop(e, container.name)} >
toggleMultiModeSelection(container.id, e)} class="w-4 h-4 shrink-0 flex items-center justify-center" title="Select for merge" > {#if isMultiSelected}
{:else}
{/if}
{container.name}
{container.image}
toggleFavorite(container.name, e)} class="p-0.5 rounded hover:bg-amber-500/20 transition-colors" title="Remove from favorites" >
{/each}
{/if} {#if layoutMode === 'grouped' && favoriteContainers().length > 0}
Favorites
{#each favoriteContainers() as container} {@const isSelected = selectedContainerIds.has(container.id)} {@const containerColor = groupedContainerInfo.get(container.id)?.color}
toggleContainerSelection(container.id)} onkeydown={(e) => e.key === 'Enter' && toggleContainerSelection(container.id)} role="button" tabindex="0" >
{#if isSelected}
{:else}
{/if}
{container.name}
{container.image}
toggleFavorite(container.name, e)} class="p-0.5 rounded hover:bg-amber-500/20 transition-colors" title="Remove from favorites" >
{/each}
{/if} {#if layoutMode === 'multi' ? nonFavoriteContainers().length > 0 : (layoutMode === 'grouped' ? nonFavoriteContainers().length > 0 : filteredContainers().length > 0)} {#if (layoutMode === 'multi' || layoutMode === 'grouped') && favoriteContainers().length > 0}
All containers
{/if} {#each layoutMode === 'multi' || layoutMode === 'grouped' ? nonFavoriteContainers() : filteredContainers() as container} {@const isSelected = layoutMode === 'grouped' ? selectedContainerIds.has(container.id) : selectedContainer?.id === container.id} {@const isMultiSelected = multiModeSelections.has(container.id)} {@const containerColor = groupedContainerInfo.get(container.id)?.color}
layoutMode === 'grouped' ? toggleContainerSelection(container.id) : selectContainer(container)} onkeydown={(e) => e.key === 'Enter' && (layoutMode === 'grouped' ? toggleContainerSelection(container.id) : selectContainer(container))} role="button" tabindex="0" > {#if layoutMode === 'grouped'}
{#if isSelected}
{:else}
{/if}
{:else if layoutMode === 'multi'}
toggleMultiModeSelection(container.id, e)} class="w-4 h-4 shrink-0 flex items-center justify-center" title="Select for merge" > {#if isMultiSelected}
{:else}
{/if}
{/if}
{container.name}
{container.image}
{#if layoutMode === 'multi'}
toggleFavorite(container.name, e)} class="p-0.5 rounded hover:bg-amber-500/20 transition-colors opacity-30 group-hover:opacity-100" title="Add to favorites" >
{/if}
{/each} {/if} {/if}
{#if layoutMode === 'grouped'}
{selectedContainerIds.size} selected
{containers.length} total
{#if selectedContainerIds.size > 0} {#if showSaveGroupInput}
e.key === 'Enter' && saveCurrentGroup()} use:focusOnMount class="h-6 text-xs flex-1 px-2 rounded border border-input bg-background focus:outline-none focus:ring-1 focus:ring-ring" />
{ showSaveGroupInput = false; newGroupName = ''; }} class="h-6 w-6 flex items-center justify-center rounded hover:bg-red-500/20 text-muted-foreground hover:text-red-500 transition-colors" title="Cancel" >
{:else}
showSaveGroupInput = true} class="w-full h-7 gap-1.5 text-xs">
Save group
{/if} {/if}
{:else}
{#if multiModeSelections.size > 0}
{multiModeSelections.size} selected
Clear
{:else}
{containers.length} container{containers.length !== 1 ? 's' : ''}
{/if}
{#if multiModeSelections.size >= 2}
Merge {multiModeSelections.size} containers
{/if}
{/if}
{/if}
{#if layoutMode === 'grouped'} {#if selectedContainerIds.size === 0}
Select containers from the list to view merged logs
{:else}
{#if streamingEnabled} {#if isConnected}
Live
{:else if loading}
Connecting...
{:else if connectionError}
Disconnected
{/if} {:else}
Paused
{/if}
{#if stackName}
{stackName}
{:else if groupedContainerInfo.size === 1} {@const singleContainer = Array.from(groupedContainerInfo.values())[0]}
{singleContainer.name}
{/if} {#if stackName || groupedContainerInfo.size > 1} {#each Array.from(groupedContainerInfo.entries()) as [id, info]}
{/each} {/if}
{#if streamingEnabled}
Pause
{:else}
Stream
{/if}
{ autoScroll = !autoScroll; saveState(); }} class="flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors {autoScroll ? (darkMode ? 'bg-amber-500/20 ring-1 ring-amber-500/50 text-amber-400' : 'bg-amber-500/30 ring-1 ring-amber-600/50 text-amber-700') : darkMode ? 'text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-200'}" title="Toggle auto-scroll" >
Auto-scroll
{ fontSize = Number(v); saveState(); }}>
{fontSize}px
{#each fontSizeOptions as size}
{size}px
{/each}
{ wordWrap = !wordWrap; saveState(); }} class="flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors {wordWrap ? (darkMode ? 'bg-amber-500/20 ring-1 ring-amber-500/50 text-amber-400' : 'bg-amber-500/30 ring-1 ring-amber-600/50 text-amber-700') : darkMode ? 'text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-200'}" title="Toggle word wrap" >
Wrap
{#if darkMode}
{:else}
{/if}
{#if logSearchActive}
{#if matchCount > 0}
{currentMatchIndex + 1}/{matchCount}
{:else if logSearchQuery}
0/0
{/if}
navigateMatch('prev')} class="p-0.5 rounded transition-colors {darkMode ? 'hover:bg-zinc-700' : 'hover:bg-gray-300'}" title="Previous match">
navigateMatch('next')} class="p-0.5 rounded transition-colors {darkMode ? 'hover:bg-zinc-700' : 'hover:bg-gray-300'}" title="Next match">
{:else}
{/if}
{#if loading && mergedLogs.length === 0}
{:else if loading}
{/if}
{#each formattedMergedLogs() as log}
[{log.containerName}]
{@html log.formattedText}
{/each}
{/if} {:else if !selectedContainer}
{layoutMode === 'multi' ? 'Select a container from the list' : 'Select a container to view logs'}
{:else}
{#if streamingEnabled} {#if isConnected}
Live
{:else if loading}
Connecting...
{:else if connectionError}
Disconnected
{:else}
Offline
{/if} {:else}
Paused
{/if} {#if selectedContainer}
{selectedContainer.name}
{/if}
{#if streamingEnabled}
Pause
{:else}
Stream
{/if}
{ autoScroll = !autoScroll; saveState(); }} class="flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors {autoScroll ? (darkMode ? 'bg-amber-500/20 ring-1 ring-amber-500/50 text-amber-400' : 'bg-amber-500/30 ring-1 ring-amber-600/50 text-amber-700') : darkMode ? 'text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-200'}" title="Toggle auto-scroll" >
Auto-scroll
{ fontSize = Number(v); saveState(); }}>
{fontSize}px
{#each fontSizeOptions as size}
{size}px
{/each}
{ wordWrap = !wordWrap; saveState(); }} class="flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors {wordWrap ? (darkMode ? 'bg-amber-500/20 ring-1 ring-amber-500/50 text-amber-400' : 'bg-amber-500/30 ring-1 ring-amber-600/50 text-amber-700') : darkMode ? 'text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-200'}" title="Toggle word wrap" >
Wrap
{#if darkMode}
{:else}
{/if}
{#if logSearchActive}
{#if matchCount > 0}
{currentMatchIndex + 1}/{matchCount}
{:else if logSearchQuery}
0/0
{/if}
navigateMatch('prev')} class="p-0.5 rounded transition-colors {darkMode ? 'hover:bg-zinc-700' : 'hover:bg-gray-300'}" title="Previous match (Shift+Enter)" >
navigateMatch('next')} class="p-0.5 rounded transition-colors {darkMode ? 'hover:bg-zinc-700' : 'hover:bg-gray-300'}" title="Next match (Enter)" >
{:else}
{/if}
fetchLogs()} class="p-1 rounded transition-colors {darkMode ? 'hover:bg-zinc-800' : 'hover:bg-gray-200'}" title="Refresh logs" >
{#if loading && !logs}
Loading logs...
{:else}
{@html highlightedLogs()}
{/if} {/if}
{/if}