Skip to content

Commit 1c3b830

Browse files
pujitmclaude
andcommitted
fix(docker): improve logs and console UX when container is stopped
- Change "Exited" status label to "Stopped" for better clarity - Show "Container is not running" message in console tab when stopped - Dim historical logs when container is stopped with info message - Remove redundant status badge from container detail header - Console tab indicator only shows green when running with active session Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 38a6f0c commit 1c3b830

File tree

5 files changed

+57
-19
lines changed

5 files changed

+57
-19
lines changed

web/src/components/Docker/ContainerOverviewCard.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ const stateColor = computed(() => {
4040
const stateLabel = computed(() => {
4141
const state = props.container?.state;
4242
if (!state) return 'Unknown';
43-
return state.charAt(0).toUpperCase() + state.slice(1);
43+
if (state === 'EXITED') return 'Stopped';
44+
return state.charAt(0).toUpperCase() + state.slice(1).toLowerCase();
4445
});
4546
4647
const imageVersion = computed(() => formatImage(props.container));

web/src/components/Docker/DockerConsoleViewer.vue

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import { useDockerConsoleSessions } from '@/composables/useDockerConsoleSessions
66
interface Props {
77
containerName: string;
88
shell?: string;
9+
isRunning?: boolean;
910
}
1011
1112
const props = withDefaults(defineProps<Props>(), {
1213
shell: 'sh',
14+
isRunning: true,
1315
});
1416
1517
const { getSession, createSession, showSession, hideSession, destroySession, markPoppedOut } =
@@ -26,7 +28,9 @@ const socketPath = computed(() => {
2628
return `/logterminal/${encodedName}/`;
2729
});
2830
29-
const showPlaceholder = computed(() => !isConnecting.value && !hasError.value && !isPoppedOut.value);
31+
const showPlaceholder = computed(
32+
() => props.isRunning && !isConnecting.value && !hasError.value && !isPoppedOut.value
33+
);
3034
3135
function updatePosition() {
3236
if (placeholderRef.value && showPlaceholder.value) {
@@ -36,6 +40,13 @@ function updatePosition() {
3640
}
3741
3842
async function initTerminal() {
43+
if (!props.isRunning) {
44+
isConnecting.value = false;
45+
hasError.value = false;
46+
isPoppedOut.value = false;
47+
return;
48+
}
49+
3950
const existingSession = getSession(props.containerName);
4051
4152
if (existingSession && !existingSession.isPoppedOut) {
@@ -93,6 +104,17 @@ watch(
93104
}
94105
);
95106
107+
watch(
108+
() => props.isRunning,
109+
(running) => {
110+
if (running) {
111+
initTerminal();
112+
} else {
113+
hideSession(props.containerName);
114+
}
115+
}
116+
);
117+
96118
watch(showPlaceholder, (show) => {
97119
if (show) {
98120
requestAnimationFrame(updatePosition);
@@ -152,7 +174,14 @@ onBeforeUnmount(() => {
152174
</div>
153175
</div>
154176

155-
<div v-if="isConnecting" class="flex flex-1 items-center justify-center rounded-lg bg-black">
177+
<div v-if="!isRunning" class="flex flex-1 items-center justify-center rounded-lg bg-black">
178+
<div class="text-center">
179+
<UIcon name="i-lucide-terminal" class="h-8 w-8 text-neutral-500" />
180+
<p class="mt-2 text-sm text-neutral-400">Container is not running</p>
181+
</div>
182+
</div>
183+
184+
<div v-else-if="isConnecting" class="flex flex-1 items-center justify-center rounded-lg bg-black">
156185
<div class="text-center">
157186
<UIcon name="i-lucide-loader-2" class="h-8 w-8 animate-spin text-green-400" />
158187
<p class="mt-2 text-sm text-green-400">Connecting to container...</p>

web/src/components/Docker/DockerContainerManagement.vue

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -381,16 +381,23 @@ const hasActiveConsoleSession = computed(() => {
381381
return name ? hasActiveSession(name) : false;
382382
});
383383
384+
const isContainerRunning = computed(() => activeContainer.value?.state === 'RUNNING');
385+
386+
const consoleBadge = computed(() => {
387+
if (isContainerRunning.value && hasActiveConsoleSession.value) {
388+
return { color: 'success' as const, variant: 'solid' as const, class: 'w-2 h-2 p-0 min-w-0' };
389+
}
390+
return undefined;
391+
});
392+
384393
const legacyPaneTabs = computed(() => [
385394
{ label: 'Overview', value: 'overview' as const },
386395
{ label: 'Settings', value: 'settings' as const },
387396
{ label: 'Logs', value: 'logs' as const },
388397
{
389398
label: 'Console',
390399
value: 'console' as const,
391-
badge: hasActiveConsoleSession.value
392-
? { color: 'success' as const, variant: 'solid' as const, class: 'w-2 h-2 p-0 min-w-0' }
393-
: undefined,
400+
badge: consoleBadge.value,
394401
},
395402
]);
396403
@@ -550,12 +557,6 @@ const [transitionContainerRef] = useAutoAnimate({
550557
{{ stripLeadingSlash(activeContainer?.names?.[0]) || 'Container' }}
551558
</div>
552559
</div>
553-
<UBadge
554-
v-if="activeContainer?.state"
555-
:label="activeContainer.state"
556-
color="primary"
557-
variant="subtle"
558-
/>
559560
</div>
560561
<UTabs
561562
v-model="legacyPaneTab"
@@ -606,6 +607,7 @@ const [transitionContainerRef] = useAutoAnimate({
606607
:container-name="stripLeadingSlash(activeContainer.names?.[0])"
607608
:auto-scroll="logAutoScroll"
608609
:client-filter="logFilterText"
610+
:is-running="isContainerRunning"
609611
class="h-full flex-1"
610612
/>
611613
</div>
@@ -617,6 +619,7 @@ const [transitionContainerRef] = useAutoAnimate({
617619
v-if="activeContainer"
618620
:container-name="activeContainerName"
619621
:shell="activeContainer.shell ?? 'sh'"
622+
:is-running="isContainerRunning"
620623
class="h-full"
621624
/>
622625
</div>
@@ -636,12 +639,6 @@ const [transitionContainerRef] = useAutoAnimate({
636639
/>
637640
<div class="font-medium">Overview</div>
638641
</div>
639-
<UBadge
640-
v-if="activeContainer?.state"
641-
:label="activeContainer.state"
642-
color="primary"
643-
variant="subtle"
644-
/>
645642
</div>
646643
</template>
647644
<div class="relative">
@@ -684,6 +681,7 @@ const [transitionContainerRef] = useAutoAnimate({
684681
v-if="activeContainer"
685682
:container-name="stripLeadingSlash(activeContainer.names?.[0])"
686683
:auto-scroll="true"
684+
:is-running="isContainerRunning"
687685
class="h-full"
688686
/>
689687
</div>

web/src/components/Docker/SingleDockerLogViewer.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ const props = withDefaults(
1616
containerName: string;
1717
autoScroll: boolean;
1818
clientFilter?: string;
19+
isRunning?: boolean;
1920
}>(),
2021
{
2122
clientFilter: '',
23+
isRunning: true,
2224
}
2325
);
2426
@@ -201,6 +203,8 @@ defineExpose({ refreshLogContent });
201203
:auto-scroll="autoScroll"
202204
:show-refresh="true"
203205
:show-download="false"
206+
:dimmed="!isRunning"
207+
:additional-info="!isRunning ? 'Container stopped - showing historical logs' : ''"
204208
@refresh="refreshLogContent"
205209
/>
206210
</template>

web/src/components/Logs/BaseLogViewer.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface Props {
2727
loadingMoreContent?: boolean;
2828
isAtTop?: boolean;
2929
canLoadMore?: boolean;
30+
dimmed?: boolean;
3031
}
3132
3233
const props = withDefaults(defineProps<Props>(), {
@@ -42,6 +43,7 @@ const props = withDefaults(defineProps<Props>(), {
4243
loadingMoreContent: false,
4344
isAtTop: false,
4445
canLoadMore: false,
46+
dimmed: false,
4547
});
4648
4749
const emit = defineEmits<{
@@ -168,7 +170,11 @@ defineExpose({ forceScrollToBottom, scrollViewportRef });
168170

169171
<pre
170172
class="hljs m-0 p-4 font-mono text-xs leading-6 whitespace-pre"
171-
:class="{ 'theme-dark': isDarkMode, 'theme-light': !isDarkMode }"
173+
:class="{
174+
'theme-dark': isDarkMode,
175+
'theme-light': !isDarkMode,
176+
'opacity-50': dimmed,
177+
}"
172178
v-html="highlightedContent"
173179
/>
174180
</div>

0 commit comments

Comments
 (0)