bloby-bot 0.63.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,80 +28,27 @@ export default function App() {
28
28
  const [showOnboard, setShowOnboard] = useState(false);
29
29
  const [userName, setUserName] = useState('');
30
30
  const [botName, setBotName] = useState('Bloby');
31
- const [rebuildState, setRebuildState] = useState<'idle' | 'rebuilding' | 'error'>('idle');
32
- const [buildError, setBuildError] = useState('');
33
31
 
34
- // ── Seamless reload: splash screen + freeze-thaw ──────────────────
35
- // Prevents the "white flash" and "delayed reload" jank that plagues PWAs.
36
- //
37
- // How it works:
38
- // 1. Any location.reload() shows the HTML splash BEFORE reloading
39
- // so the user sees: app → splash → splash app (no white gap).
40
- // 2. When returning from background (> 30s hidden), the splash shows
41
- // IMMEDIATELY — before Vite's delayed reconnect-reload can fire.
42
- // If no reload comes within 3s, the splash fades away.
43
- // 3. Vite full-reloads trigger the splash too (same mechanism).
32
+ // ── Seamless reload: splash before full-reloads ───────────────────
33
+ // Vite's reload-on-reconnect is suppressed by the supervisor-injected
34
+ // workspace-guard (which also owns location.reload wrapping), so the
35
+ // only reloads left are real ones: Vite full-reloads after frontend
36
+ // changes. Show the HTML splash before those so the user sees
37
+ // app → splash → app instead of a white flash.
44
38
  useEffect(() => {
45
39
  const splash = document.getElementById('splash');
46
- let hiddenAt = 0;
47
40
 
48
- // Show the dark background (used before reloads and on resume)
41
+ // Show the dark background before the reload unloads the page
49
42
  function showSplash() {
50
43
  if (!splash) return;
51
44
  splash.style.display = 'block';
52
45
  splash.style.opacity = '1';
53
46
  }
54
47
 
55
- // Hide the splash screen with a fade
56
- function hideSplash() {
57
- if (!splash || splash.style.display === 'none') return;
58
- splash.style.opacity = '0';
59
- splash.addEventListener('transitionend', () => { splash.style.display = 'none'; }, { once: true });
60
- }
61
-
62
- // Wrap location.reload: show splash, wait for paint, then reload.
63
- // This ensures the dark splash is visible during the brief unload→load gap.
64
- try {
65
- const origReload = location.reload.bind(location);
66
- Object.defineProperty(location, 'reload', {
67
- configurable: true,
68
- value: () => {
69
- showSplash();
70
- requestAnimationFrame(() => requestAnimationFrame(() => origReload()));
71
- },
72
- });
73
- } catch {
74
- // location.reload is non-configurable in some browsers — skip override
75
- }
76
-
77
48
  // Vite HMR: show splash before a full-reload
78
49
  if (import.meta.hot) {
79
50
  import.meta.hot.on('vite:beforeFullReload', () => showSplash());
80
51
  }
81
-
82
- // Freeze-thaw: when returning from background after > 30s,
83
- // show splash proactively so the user never sees the "working app
84
- // suddenly yank away" pattern. If Vite decides to full-reload,
85
- // the splash is already visible. If not, we fade it away after 3s.
86
- let thawTimer: ReturnType<typeof setTimeout>;
87
- const BACKGROUND_THRESHOLD = 30_000; // 30 seconds
88
-
89
- const onVisChange = () => {
90
- if (document.visibilityState === 'hidden') {
91
- hiddenAt = Date.now();
92
- } else if (hiddenAt && Date.now() - hiddenAt > BACKGROUND_THRESHOLD) {
93
- showSplash();
94
- // Give Vite 3s to trigger a reload; if it doesn't, hide splash
95
- clearTimeout(thawTimer);
96
- thawTimer = setTimeout(hideSplash, 3_000);
97
- }
98
- };
99
- document.addEventListener('visibilitychange', onVisChange);
100
-
101
- return () => {
102
- document.removeEventListener('visibilitychange', onVisChange);
103
- clearTimeout(thawTimer);
104
- };
105
52
  }, []);
106
53
 
107
54
  useEffect(() => {
@@ -119,39 +66,18 @@ export default function App() {
119
66
  .catch(() => {});
120
67
  }, []);
121
68
 
122
- // Listen for rebuild events from Bloby iframe via postMessage
69
+ // Listen for events from Bloby iframes via postMessage.
70
+ // 'bloby:hmr-update' is intentionally ignored: Vite HMR handles hot
71
+ // updates natively, and a manual location.reload() here would kill the
72
+ // chat iframe's WebSocket connection for nothing.
123
73
  useEffect(() => {
124
- let safetyTimer: ReturnType<typeof setTimeout>;
125
74
  const handler = (e: MessageEvent) => {
126
- if (e.data?.type === 'bloby:rebuilding') {
127
- setRebuildState('rebuilding');
128
- setBuildError('');
129
- // Safety: auto-reload after 60s in case app:rebuilt message is lost
130
- clearTimeout(safetyTimer);
131
- safetyTimer = setTimeout(() => location.reload(), 60_000);
132
- } else if (e.data?.type === 'bloby:rebuilt') {
133
- clearTimeout(safetyTimer);
134
- setRebuildState('idle');
135
- location.reload();
136
- } else if (e.data?.type === 'bloby:build-error') {
137
- clearTimeout(safetyTimer);
138
- setRebuildState('error');
139
- setBuildError(e.data.error || 'Build failed');
140
- setTimeout(() => setRebuildState('idle'), 5000);
141
- } else if (e.data?.type === 'bloby:onboard-complete') {
75
+ if (e.data?.type === 'bloby:onboard-complete') {
142
76
  setShowOnboard(false);
143
- } else if (e.data?.type === 'bloby:hmr-update') {
144
- // Vite HMR handles hot updates natively — no manual reload needed.
145
- // Manual location.reload() here was causing unnecessary full-page refreshes
146
- // that killed the chat iframe's WebSocket connection.
147
-
148
77
  }
149
78
  };
150
79
  window.addEventListener('message', handler);
151
- return () => {
152
- window.removeEventListener('message', handler);
153
- clearTimeout(safetyTimer);
154
- };
80
+ return () => window.removeEventListener('message', handler);
155
81
  }, []);
156
82
 
157
83
  return (
@@ -172,22 +98,6 @@ export default function App() {
172
98
  style={{ position: 'fixed', inset: 0, width: '100vw', height: '100dvh', border: 'none', zIndex: 200 }}
173
99
  />
174
100
  )}
175
-
176
- {rebuildState !== 'idle' && (
177
- <div className="fixed inset-0 z-[49] flex flex-col items-center justify-center bg-background/90">
178
- <video
179
- src="/bloby_tilts.webm"
180
- autoPlay
181
- loop
182
- muted
183
- playsInline
184
- className="h-24 w-24 rounded-full object-cover"
185
- />
186
- <p className="mt-4 text-sm text-muted-foreground">
187
- {rebuildState === 'rebuilding' ? 'Rebuilding app...' : buildError}
188
- </p>
189
- </div>
190
- )}
191
101
  </>
192
102
  );
193
103
  }
@@ -14,14 +14,11 @@ export default function DashboardLayout({ children, userName, botName = 'Bloby'
14
14
 
15
15
  useEffect(() => {
16
16
  const check = () => {
17
- console.log('[health] checking /app/api/health…');
18
17
  fetch('/app/api/health', { signal: AbortSignal.timeout(3000) })
19
18
  .then((r) => {
20
- console.log(`[health] response: ${r.status} ok=${r.ok}`);
21
19
  setStatus(r.ok ? 'healthy' : 'restarting');
22
20
  })
23
21
  .catch((err) => {
24
- console.warn('[health] fetch failed:', err.message ?? err);
25
22
  setStatus('restarting');
26
23
  });
27
24
  };