fix(terminal): detect shell tab visibility via MutationObserver
All checks were successful
Beta Release / beta (push) Successful in 49s

Shell is always mounted inside a display:none parent when the app
loads on a different tab. Added MutationObserver on the wrapper to
detect when the shell tab becomes visible and initialize/fit all
pending terminals at that moment. Removed attempt limit so retries
continue until the tab is actually shown.

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-24 20:28:02 +02:00
parent 13e937a11b
commit 08dc1fd53b

View File

@@ -425,15 +425,36 @@ export default function Shell({ api }) {
} }
}, []) }, [])
const initPendingTabs = useCallback(() => {
for (const tab of tabsRef.current._tabList || []) {
if (!tabsRef.current[tab.id]) {
const container = document.getElementById(`terminal-${tab.id}`)
if (container && container.offsetHeight > 0) {
initTerminal(tab.id, tab)
}
}
}
requestAnimationFrame(() => {
for (const tab of tabsRef.current._tabList || []) {
const entry = tabsRef.current[tab.id]
if (entry) entry.fitAddon.fit()
}
})
}, [initTerminal])
useEffect(() => {
tabsRef.current._tabList = tabs
}, [tabs])
useEffect(() => { useEffect(() => {
let cancelled = false let cancelled = false
const pending = [] const pending = []
const tryInitTab = (tab, attempt) => { const tryInitTab = (tab, attempt) => {
if (cancelled || attempt > 30) return if (cancelled) return
const shellCol = document.querySelector('.shell-terminal-col') const shellCol = document.querySelector('.shell-terminal-col')
if (!shellCol || shellCol.offsetParent === null) { if (!shellCol || shellCol.offsetParent === null) {
pending.push(setTimeout(() => tryInitTab(tab, attempt + 1), 150)) pending.push(setTimeout(() => tryInitTab(tab, attempt + 1), 200))
return return
} }
const container = document.getElementById(`terminal-${tab.id}`) const container = document.getElementById(`terminal-${tab.id}`)
@@ -461,11 +482,23 @@ export default function Shell({ api }) {
} }
} }
const wrapper = document.querySelector('.shell-layout')?.parentElement
let observer
if (wrapper) {
observer = new MutationObserver(() => {
if (!wrapper.classList.contains('tab-hidden') && wrapper.offsetParent !== null) {
initPendingTabs()
}
})
observer.observe(wrapper, { attributes: true, attributeFilter: ['class'] })
}
return () => { return () => {
cancelled = true cancelled = true
pending.forEach(clearTimeout) pending.forEach(clearTimeout)
observer?.disconnect()
} }
}, [tabs, initTerminal]) }, [tabs, initTerminal, initPendingTabs])
useEffect(() => { useEffect(() => {
const entry = tabsRef.current[activeTab] const entry = tabsRef.current[activeTab]
@@ -480,6 +513,8 @@ export default function Shell({ api }) {
useEffect(() => { useEffect(() => {
const iv = setInterval(() => { const iv = setInterval(() => {
const wrapper = document.querySelector('.shell-layout')?.parentElement
if (wrapper && wrapper.classList.contains('tab-hidden')) return
const entry = tabsRef.current[activeTabRef.current] const entry = tabsRef.current[activeTabRef.current]
if (entry) { if (entry) {
entry.fitAddon.fit() entry.fitAddon.fit()