{{ mustOpenNewTab ? 'This app opens in a new tab' : 'App not reachable' }}
{{ appTitle }} sets security headers that prevent iframe embedding. Open it in a new browser tab instead.
- {{ appTitle }} may still be starting up or the container is stopped. Try opening in a new tab or check the app status.
+ {{ appTitle }} may still be starting up or the container is stopped. Retrying automatically ({{ autoRetryCount }})...
-
+
+
+
+
@@ -211,7 +223,9 @@ const iframeBlocked = ref(false)
const refreshKey = ref(0)
const showIdentityPicker = ref(false)
const showModeMenu = ref(false)
+const autoRetryCount = ref(0)
let loadTimeoutId: ReturnType | null = null
+let autoRetryId: ReturnType | null = null
/** Sites known to block iframes — skip the timeout and go straight to fallback */
const IFRAME_BLOCKED_APPS = new Set([])
@@ -483,8 +497,10 @@ async function sendIdentity(identity: SelectedIdentity) {
function onLoad() {
if (loadTimeoutId) { clearTimeout(loadTimeoutId); loadTimeoutId = null }
+ if (autoRetryId) { clearTimeout(autoRetryId); autoRetryId = null }
loading.value = false
isRefreshing.value = false
+ autoRetryCount.value = 0
// Check if iframe actually loaded content (same-origin only)
setTimeout(() => {
try {
@@ -511,9 +527,17 @@ function onError() {
loading.value = false
isRefreshing.value = false
iframeBlocked.value = true
+ // Auto-retry up to 6 times (60s total) for apps that are still starting
+ if (!mustOpenNewTab.value && autoRetryCount.value < 6) {
+ autoRetryId = setTimeout(() => {
+ autoRetryCount.value++
+ refresh()
+ }, 10000)
+ }
}
function refresh() {
+ if (autoRetryId) { clearTimeout(autoRetryId); autoRetryId = null }
isRefreshing.value = true
loading.value = true
iframeBlocked.value = false
@@ -672,6 +696,7 @@ onMounted(() => {
onBeforeUnmount(() => {
if (loadTimeoutId) clearTimeout(loadTimeoutId)
+ if (autoRetryId) clearTimeout(autoRetryId)
window.removeEventListener('keydown', onKeyDown, true)
window.removeEventListener('message', onMessage)
document.removeEventListener('click', onClickOutside)