pi-studio 0.5.7 → 0.5.8
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.
- package/CHANGELOG.md +6 -0
- package/index.ts +92 -21
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.8] — 2026-03-12
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Studio browser tabs now auto-reconnect after unexpected websocket disconnects (for example transient local connection loss or sleep/wake), while intentional invalidation/shutdown still requires a fresh `/studio`.
|
|
11
|
+
- Same-tab reconnect now preserves the currently selected response-history item instead of jumping back to the latest response on every `hello_ack` resync.
|
|
12
|
+
|
|
7
13
|
## [0.5.7] — 2026-03-12
|
|
8
14
|
|
|
9
15
|
### Changed
|
package/index.ts
CHANGED
|
@@ -3301,6 +3301,8 @@ ${cssVarsBlock}
|
|
|
3301
3301
|
let wsState = "Connecting";
|
|
3302
3302
|
let statusMessage = "Connecting · Studio script starting…";
|
|
3303
3303
|
let statusLevel = "";
|
|
3304
|
+
let reconnectTimer = null;
|
|
3305
|
+
let reconnectAttempt = 0;
|
|
3304
3306
|
let pendingRequestId = null;
|
|
3305
3307
|
let pendingKind = null;
|
|
3306
3308
|
let stickyStudioKind = null;
|
|
@@ -5824,8 +5826,8 @@ ${cssVarsBlock}
|
|
|
5824
5826
|
let appliedHistory = false;
|
|
5825
5827
|
if (Array.isArray(message.responseHistory)) {
|
|
5826
5828
|
appliedHistory = setResponseHistory(message.responseHistory, {
|
|
5827
|
-
autoSelectLatest:
|
|
5828
|
-
preserveSelection:
|
|
5829
|
+
autoSelectLatest: !initialDocumentApplied,
|
|
5830
|
+
preserveSelection: initialDocumentApplied,
|
|
5829
5831
|
silent: true,
|
|
5830
5832
|
});
|
|
5831
5833
|
}
|
|
@@ -6184,7 +6186,42 @@ ${cssVarsBlock}
|
|
|
6184
6186
|
}
|
|
6185
6187
|
}
|
|
6186
6188
|
|
|
6189
|
+
function clearScheduledReconnect() {
|
|
6190
|
+
if (reconnectTimer !== null) {
|
|
6191
|
+
window.clearTimeout(reconnectTimer);
|
|
6192
|
+
reconnectTimer = null;
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
6195
|
+
|
|
6196
|
+
function formatReconnectDelay(delayMs) {
|
|
6197
|
+
const delay = Math.max(0, Number(delayMs) || 0);
|
|
6198
|
+
if (delay < 1000) return delay + "ms";
|
|
6199
|
+
const seconds = delay / 1000;
|
|
6200
|
+
return (Number.isInteger(seconds) ? String(seconds) : seconds.toFixed(1)) + "s";
|
|
6201
|
+
}
|
|
6202
|
+
|
|
6203
|
+
function scheduleReconnect(reasonMessage) {
|
|
6204
|
+
if (reconnectTimer !== null) return;
|
|
6205
|
+
|
|
6206
|
+
reconnectAttempt += 1;
|
|
6207
|
+
const delayMs = Math.min(8000, 600 * Math.pow(2, Math.max(0, reconnectAttempt - 1)));
|
|
6208
|
+
setBusy(true);
|
|
6209
|
+
setWsState("Connecting");
|
|
6210
|
+
setStatus((reasonMessage || "Connection lost.") + " Reconnecting in " + formatReconnectDelay(delayMs) + "…", "warning");
|
|
6211
|
+
|
|
6212
|
+
reconnectTimer = window.setTimeout(() => {
|
|
6213
|
+
reconnectTimer = null;
|
|
6214
|
+
connect();
|
|
6215
|
+
}, delayMs);
|
|
6216
|
+
}
|
|
6217
|
+
|
|
6187
6218
|
function connect() {
|
|
6219
|
+
clearScheduledReconnect();
|
|
6220
|
+
|
|
6221
|
+
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
|
|
6222
|
+
return;
|
|
6223
|
+
}
|
|
6224
|
+
|
|
6188
6225
|
const token = getToken();
|
|
6189
6226
|
if (!token) {
|
|
6190
6227
|
setWsState("Disconnected");
|
|
@@ -6195,26 +6232,61 @@ ${cssVarsBlock}
|
|
|
6195
6232
|
|
|
6196
6233
|
const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
|
|
6197
6234
|
const wsUrl = wsProtocol + "://" + window.location.host + "/ws?token=" + encodeURIComponent(token) + (DEBUG_ENABLED ? "&debug=1" : "");
|
|
6235
|
+
const wasReconnect = reconnectAttempt > 0;
|
|
6236
|
+
let disconnectHandled = false;
|
|
6198
6237
|
|
|
6199
6238
|
setWsState("Connecting");
|
|
6200
|
-
setStatus("Connecting to Studio server…");
|
|
6201
|
-
|
|
6239
|
+
setStatus(wasReconnect ? "Reconnecting to Studio server…" : "Connecting to Studio server…");
|
|
6240
|
+
const socket = new WebSocket(wsUrl);
|
|
6241
|
+
ws = socket;
|
|
6202
6242
|
|
|
6203
6243
|
const connectWatchdog = window.setTimeout(() => {
|
|
6204
|
-
if (ws &&
|
|
6244
|
+
if (ws === socket && socket.readyState === WebSocket.CONNECTING) {
|
|
6205
6245
|
setWsState("Connecting");
|
|
6206
|
-
setStatus("Still connecting…", "warning");
|
|
6246
|
+
setStatus(wasReconnect ? "Still reconnecting…" : "Still connecting…", "warning");
|
|
6207
6247
|
}
|
|
6208
6248
|
}, 3000);
|
|
6209
6249
|
|
|
6210
|
-
|
|
6250
|
+
const handleDisconnect = (kind, code) => {
|
|
6251
|
+
if (disconnectHandled) return;
|
|
6252
|
+
disconnectHandled = true;
|
|
6253
|
+
window.clearTimeout(connectWatchdog);
|
|
6254
|
+
if (ws === socket) {
|
|
6255
|
+
ws = null;
|
|
6256
|
+
}
|
|
6257
|
+
setBusy(true);
|
|
6258
|
+
|
|
6259
|
+
if (kind === "invalidated") {
|
|
6260
|
+
clearScheduledReconnect();
|
|
6261
|
+
reconnectAttempt = 0;
|
|
6262
|
+
setWsState("Disconnected");
|
|
6263
|
+
setStatus("This tab was invalidated by a newer /studio session.", "warning");
|
|
6264
|
+
return;
|
|
6265
|
+
}
|
|
6266
|
+
|
|
6267
|
+
if (kind === "shutdown") {
|
|
6268
|
+
clearScheduledReconnect();
|
|
6269
|
+
reconnectAttempt = 0;
|
|
6270
|
+
setWsState("Disconnected");
|
|
6271
|
+
setStatus("Studio server shut down. Re-run /studio.", "warning");
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
|
|
6275
|
+
const detail = typeof code === "number" && code > 0
|
|
6276
|
+
? "Disconnected (code " + code + ")."
|
|
6277
|
+
: (kind === "error" ? "WebSocket error." : "Connection lost.");
|
|
6278
|
+
scheduleReconnect(detail);
|
|
6279
|
+
};
|
|
6280
|
+
|
|
6281
|
+
socket.addEventListener("open", () => {
|
|
6211
6282
|
window.clearTimeout(connectWatchdog);
|
|
6212
6283
|
setWsState("Ready");
|
|
6213
|
-
setStatus("Connected. Syncing…");
|
|
6284
|
+
setStatus(wasReconnect ? "Reconnected. Syncing…" : "Connected. Syncing…");
|
|
6214
6285
|
sendMessage({ type: "hello" });
|
|
6286
|
+
reconnectAttempt = 0;
|
|
6215
6287
|
});
|
|
6216
6288
|
|
|
6217
|
-
|
|
6289
|
+
socket.addEventListener("message", (event) => {
|
|
6218
6290
|
try {
|
|
6219
6291
|
const message = JSON.parse(event.data);
|
|
6220
6292
|
handleServerMessage(message);
|
|
@@ -6224,22 +6296,21 @@ ${cssVarsBlock}
|
|
|
6224
6296
|
}
|
|
6225
6297
|
});
|
|
6226
6298
|
|
|
6227
|
-
|
|
6228
|
-
window.clearTimeout(connectWatchdog);
|
|
6229
|
-
setBusy(true);
|
|
6230
|
-
setWsState("Disconnected");
|
|
6299
|
+
socket.addEventListener("close", (event) => {
|
|
6231
6300
|
if (event && event.code === 4001) {
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6301
|
+
handleDisconnect("invalidated", 4001);
|
|
6302
|
+
return;
|
|
6303
|
+
}
|
|
6304
|
+
if (event && event.code === 1001) {
|
|
6305
|
+
handleDisconnect("shutdown", 1001);
|
|
6306
|
+
return;
|
|
6236
6307
|
}
|
|
6308
|
+
const code = event && typeof event.code === "number" ? event.code : 0;
|
|
6309
|
+
handleDisconnect("close", code);
|
|
6237
6310
|
});
|
|
6238
6311
|
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
setWsState("Disconnected");
|
|
6242
|
-
setStatus("WebSocket error. Check /studio --status and reopen.", "error");
|
|
6312
|
+
socket.addEventListener("error", () => {
|
|
6313
|
+
handleDisconnect("error");
|
|
6243
6314
|
});
|
|
6244
6315
|
}
|
|
6245
6316
|
|