Problem
When using pi-web in dev mode, the page unexpectedly does a full browser reload when opening/returning to the page on an existing session. This is highly disruptive — the user sees their conversation, moves to type, and the page refreshes.
Root Cause
Vite's HMR client (node_modules/vite/dist/client/client.mjs) has a disconnect handler that unconditionally reloads the page whenever the HMR WebSocket closes:
if (payload.event === "vite:ws:disconnect") {
if (hasDocument && !willUnload) {
console.log(`[vite] server connection lost. Polling for restart...`);
await waitForSuccessfulPing(url.href);
location.reload(); // ← full page reload
}
}
The HMR WebSocket can close due to:
- Browser background tab throttling
- TCP idle timeouts (especially through the supervisor proxy)
- Network micro-interruptions
Since the server is still running, the ping succeeds immediately and location.reload() fires. This happens even though no files changed and no server restart occurred.
The application's own /ws WebSocket has proper reconnect logic (reconnect + replay missed events, no reload), but Vite's HMR client is separate and far more aggressive.
Proposed Solution
Add a PI_WEB_HMR environment variable to toggle HMR, and expose it as a runtime toggle via the supervisor:
- Supervisor holds
hmrEnabled state (defaults from PI_WEB_HMR env var, defaults to "1" in dev)
- New supervisor endpoint:
POST /api/hmr with { enabled: boolean } — updates the flag and restarts the child
- server.ts reads the flag and passes
hmr: false or hmr: { server } to createViteServer()
- UI toggle in settings or status bar (e.g., "Enable HMR" checkbox, or
/hmr slash command)
This lets developers enable HMR only when actively working on pi-web source, and disable it for normal usage — eliminating the spurious reloads.
Partial implementation already in place
server.ts already has:
const hmrEnabled = isDev && process.env.PI_WEB_HMR !== "0";
// ...
hmr: hmrEnabled ? { server } : false,
What remains:
Workaround
Set PI_WEB_HMR=0 before starting the supervisor to disable HMR entirely (loses hot reload for development).
Problem
When using pi-web in dev mode, the page unexpectedly does a full browser reload when opening/returning to the page on an existing session. This is highly disruptive — the user sees their conversation, moves to type, and the page refreshes.
Root Cause
Vite's HMR client (
node_modules/vite/dist/client/client.mjs) has a disconnect handler that unconditionally reloads the page whenever the HMR WebSocket closes:The HMR WebSocket can close due to:
Since the server is still running, the ping succeeds immediately and
location.reload()fires. This happens even though no files changed and no server restart occurred.The application's own
/wsWebSocket has proper reconnect logic (reconnect + replay missed events, no reload), but Vite's HMR client is separate and far more aggressive.Proposed Solution
Add a
PI_WEB_HMRenvironment variable to toggle HMR, and expose it as a runtime toggle via the supervisor:hmrEnabledstate (defaults fromPI_WEB_HMRenv var, defaults to"1"in dev)POST /api/hmrwith{ enabled: boolean }— updates the flag and restarts the childhmr: falseorhmr: { server }tocreateViteServer()/hmrslash command)This lets developers enable HMR only when actively working on pi-web source, and disable it for normal usage — eliminating the spurious reloads.
Partial implementation already in place
server.tsalready has:What remains:
Workaround
Set
PI_WEB_HMR=0before starting the supervisor to disable HMR entirely (loses hot reload for development).