async function loadWorkspace({ document, window = null, createLoader = createFeatureLoader, schedule = callback => setTimeout(callback, 750), }) { let cameOnline = false; let replayed = false; const status = document.querySelector('#my-work-action-status'); const retryButton = document.querySelector('#retry-workspace'); const names = ['work-core', 'today-timer', 'planning']; const urls = Object.fromEntries(names.map(name => [name, document.querySelector(`meta[name="stackchain-feature-${name}"]`)?.content || '' ])); const originalUrls = {...urls}; const loader = createLoader({document, urls}); const attempts = Object.fromEntries(names.map(name => [name, 0])); const failed = new Set(); const recoveries = new Map(); let retryInFlight = null; const loadFeature = name => { attempts[name] += 1; urls[name] = originalUrls[name] + (attempts[name] > 1 ? '?retry=' + attempts[name] : ''); return loader.load(name); }; const retryOnce = async name => { try { return await loadFeature(name); } catch (_error) { await new Promise(resolve => schedule(resolve)); return loadFeature(name); } }; const showRecovery = () => { if (status) status.textContent = failed.has('work-core') ? 'Workspace unavailable. Reconnect or retry.' : 'Today or planning tools unavailable. My Work is ready; reconnect or retry.'; if (retryButton) retryButton.hidden = retryButton.disabled = false; }; const hideRecovery = () => { if (failed.size) return; if (retryButton) retryButton.hidden = true; if (status) status.textContent = ''; }; const retryFailed = () => { if (retryInFlight) return retryInFlight; if (retryButton) retryButton.disabled = true; const pending = Array.from(failed); retryInFlight = Promise.all(pending.map(async name => { try { await loadFeature(name); failed.delete(name); recoveries.get(name)?.resolve(true); recoveries.delete(name); } catch (_error) {} })).then(() => { if (failed.size) showRecovery(); else hideRecovery(); }).finally(() => { retryInFlight = null; }); return retryInFlight; }; retryButton?.addEventListener?.('click', retryFailed); const handleOnline = () => { cameOnline = true; void retryFailed(); }; window?.addEventListener('online', handleOnline); try { await retryOnce('work-core'); } catch (_error) { failed.add('work-core'); showRecovery(); await new Promise(resolve => recoveries.set('work-core', {resolve})); } hideRecovery(); const optional = ['today-timer', 'planning'].map(async name => { try { await retryOnce(name); return true; } catch (_error) { failed.add(name); showRecovery(); return new Promise(resolve => recoveries.set(name, {resolve})); } }); const optionalReady = Promise.all(optional).then(() => { hideRecovery(); return true; }); return { optionalReady, retryFeature(name) { if (!failed.has(name)) return Promise.resolve(true); return retryFailed().then(() => !failed.has(name)); }, replayOnline(callback) { if (replayed) return; replayed = true; window?.removeEventListener('online', handleOnline); if (cameOnline) callback(); }, }; } if (typeof window !== 'undefined' && typeof document !== 'undefined' && typeof createFeatureLoader === 'function') { window.stackchainWorkspaceLifecycle = loadWorkspace({document, window}); } if (typeof module !== 'undefined' && module.exports) module.exports = loadWorkspace;