Skip to content

Commit 5fcf422

Browse files
committed
Fix terminal flash after isPending turns true
1 parent 83f8aa1 commit 5fcf422

1 file changed

Lines changed: 24 additions & 22 deletions

File tree

  • apps/web/src/components/service/panel/content/deployed/terminal

apps/web/src/components/service/panel/content/deployed/terminal/terminal.tsx

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,37 +68,39 @@ export default function Terminal() {
6868
};
6969
}, [isFullscreen]);
7070

71-
// Pin the instance so a background refetch can't silently move us to another pod.
72-
useEffect(() => {
73-
if (!data) return;
74-
if (data.data.length === 0) return;
75-
setSelectedPod((prev) => {
76-
if (prev && data.data.some((p) => p.kubernetes_name === prev)) return prev;
77-
return (data.data.find((p) => p.instances.some((i) => i.ready)) ?? data.data[0])
78-
.kubernetes_name;
79-
});
80-
}, [data]);
71+
// `selectedPod`/`selectedContainer` are *user overrides* layered on top of a derived default —
72+
// never the source of truth that has to be populated before anything renders. Resolving the
73+
// active pod/container purely during render means there's no frame where `data` exists but the
74+
// selection hasn't "caught up" yet, which is what used to flash the empty state.
75+
const pinnedPodRef = useRef<string | null>(null);
8176

8277
const activePod = useMemo(() => {
83-
if (!selectedPod || !data) return undefined;
84-
return (
85-
data.data.find((p) => p.kubernetes_name === selectedPod) ??
86-
data.data.find((p) => p.instances.some((i) => i.ready)) ??
87-
data.data[0]
88-
);
78+
if (!data || data.data.length === 0) return undefined;
79+
// 1. explicit user selection, if it still exists
80+
if (selectedPod) {
81+
const match = data.data.find((p) => p.kubernetes_name === selectedPod);
82+
if (match) return match;
83+
}
84+
// 2. the pod we previously resolved to, so a background refetch can't silently move us
85+
if (pinnedPodRef.current) {
86+
const match = data.data.find((p) => p.kubernetes_name === pinnedPodRef.current);
87+
if (match) return match;
88+
}
89+
// 3. first ready pod, else first pod
90+
return data.data.find((p) => p.instances.some((i) => i.ready)) ?? data.data[0];
8991
}, [data, selectedPod]);
9092

93+
// Cache the resolved pod for the pin above. This only writes a ref — it never gates rendering,
94+
// so it can't reintroduce the empty-state flash.
95+
useEffect(() => {
96+
if (activePod) pinnedPodRef.current = activePod.kubernetes_name;
97+
}, [activePod]);
98+
9199
const containers = useMemo(() => {
92100
if (!activePod) return undefined;
93101
return activePod.instances.map((i) => i.kubernetes_name);
94102
}, [activePod]);
95103

96-
useEffect(() => {
97-
if (!containers) return;
98-
if (containers.length === 0) return;
99-
setSelectedContainer((prev) => (prev && containers.includes(prev) ? prev : containers[0]));
100-
}, [containers]);
101-
102104
const activeContainer = useMemo(() => {
103105
if (!containers || containers.length === 0) return;
104106
if (selectedContainer && containers.includes(selectedContainer)) return selectedContainer;

0 commit comments

Comments
 (0)