dsh-neotui 0.1.2 → 0.1.3
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/package.json +1 -1
- package/src/api.js +15 -2
- package/src/views.js +27 -3
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -26,6 +26,7 @@ export class Api {
|
|
|
26
26
|
this.onStateChange = onStateChange;
|
|
27
27
|
this.ws = null;
|
|
28
28
|
this.hostWs = null;
|
|
29
|
+
this.muxWs = null;
|
|
29
30
|
this.closed = false;
|
|
30
31
|
this.connected = false;
|
|
31
32
|
this.retryDelay = 500;
|
|
@@ -82,7 +83,9 @@ export class Api {
|
|
|
82
83
|
#connect(url, kind) {
|
|
83
84
|
if (this.closed) return;
|
|
84
85
|
const ws = new WebSocket(url);
|
|
85
|
-
(kind === "mux"
|
|
86
|
+
if (kind === "mux") this.muxWs = ws;
|
|
87
|
+
else this.hostWs = ws;
|
|
88
|
+
this.ws = ws; // most recent socket (for diagnostics)
|
|
86
89
|
ws.onopen = () => {
|
|
87
90
|
this.connected = true;
|
|
88
91
|
this.retryDelay = 500;
|
|
@@ -130,10 +133,20 @@ export class Api {
|
|
|
130
133
|
return body.result.value;
|
|
131
134
|
}
|
|
132
135
|
|
|
136
|
+
/** Reconnect the mux stream. The host re-pushes the session baseline
|
|
137
|
+
* (session/subscribed + session/jobs snapshots) on every fresh mux
|
|
138
|
+
* connection, so this doubles as a "refresh jobs snapshots" request —
|
|
139
|
+
* e.g. when the connect-time snapshot arrived before a session opened. */
|
|
140
|
+
refreshMux() {
|
|
141
|
+
if (this.closed) return;
|
|
142
|
+
try { this.muxWs?.close(); } catch {}
|
|
143
|
+
// the onclose handler reconnects with backoff
|
|
144
|
+
}
|
|
145
|
+
|
|
133
146
|
close() {
|
|
134
147
|
this.closed = true;
|
|
135
148
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
136
|
-
try { this.
|
|
149
|
+
try { this.muxWs?.close(); } catch {}
|
|
137
150
|
try { this.hostWs?.close(); } catch {}
|
|
138
151
|
try { this.rpcWs?.close(); } catch {}
|
|
139
152
|
}
|
package/src/views.js
CHANGED
|
@@ -860,7 +860,7 @@ export class ChatView extends Widget {
|
|
|
860
860
|
case "session/title": this.title = frame.title ?? this.title; this.queueRebuild(); break;
|
|
861
861
|
case "session/jobs": {
|
|
862
862
|
this.running = (frame.jobs ?? []).some((j) => j.status === "running");
|
|
863
|
-
this.app.setJobs(frame.jobs ?? []);
|
|
863
|
+
this.app.setJobs(frame.jobs ?? [], frame.sessionId);
|
|
864
864
|
break;
|
|
865
865
|
}
|
|
866
866
|
case "session/subscribed": {
|
|
@@ -1806,6 +1806,7 @@ export class App {
|
|
|
1806
1806
|
this.toastMsg = null;
|
|
1807
1807
|
this.toastUntil = 0;
|
|
1808
1808
|
this.jobs = [];
|
|
1809
|
+
this.jobsBySession = new Map(); // sessionId → latest session/jobs snapshot
|
|
1809
1810
|
this.focused = null;
|
|
1810
1811
|
this.provider = "";
|
|
1811
1812
|
this.model = "";
|
|
@@ -1938,7 +1939,14 @@ export class App {
|
|
|
1938
1939
|
}
|
|
1939
1940
|
|
|
1940
1941
|
setStatus(msg) { this.statusMsg = msg; this.redraw(); }
|
|
1941
|
-
setJobs(jobs
|
|
1942
|
+
setJobs(jobs, sessionId = null) {
|
|
1943
|
+
// snapshot buffered per session: the mux baseline arrives at connect
|
|
1944
|
+
// time, possibly before the chat opens that session
|
|
1945
|
+
if (sessionId != null) this.jobsBySession.set(sessionId, jobs);
|
|
1946
|
+
if (sessionId == null || sessionId === this.currentSession) this.jobs = jobs;
|
|
1947
|
+
this.layout();
|
|
1948
|
+
this.redraw();
|
|
1949
|
+
}
|
|
1942
1950
|
|
|
1943
1951
|
#startPolling() {
|
|
1944
1952
|
// Self-rescheduling so the interval actually tracks run state (a fixed
|
|
@@ -2019,7 +2027,6 @@ export class App {
|
|
|
2019
2027
|
}
|
|
2020
2028
|
case "session/event":
|
|
2021
2029
|
case "session/title":
|
|
2022
|
-
case "session/jobs":
|
|
2023
2030
|
case "session/subscribed":
|
|
2024
2031
|
case "session/queue":
|
|
2025
2032
|
if (this.chat.sessionId === frame.sessionId) {
|
|
@@ -2028,6 +2035,13 @@ export class App {
|
|
|
2028
2035
|
// refresh list on title updates
|
|
2029
2036
|
if (frame.type === "session/title") this.refreshSessions();
|
|
2030
2037
|
break;
|
|
2038
|
+
case "session/jobs":
|
|
2039
|
+
// buffer every session's snapshot even before it is opened (the
|
|
2040
|
+
// connect-time baseline would otherwise be dropped by the filter
|
|
2041
|
+
// below and the footer would stick at "0已完成")
|
|
2042
|
+
this.setJobs(frame.jobs ?? [], frame.sessionId ?? null);
|
|
2043
|
+
if (this.chat.sessionId === frame.sessionId) this.chat.onFrame(frame);
|
|
2044
|
+
break;
|
|
2031
2045
|
case "session/projection":
|
|
2032
2046
|
this.projections[frame.key] = frame.value;
|
|
2033
2047
|
if (frame.key === "tokenUsage") this.tokenUsage = frame.value;
|
|
@@ -2323,6 +2337,16 @@ export class App {
|
|
|
2323
2337
|
|
|
2324
2338
|
async openSession(sessionId) {
|
|
2325
2339
|
this.currentSession = sessionId;
|
|
2340
|
+
// apply the buffered jobs snapshot; if this session's connect-time
|
|
2341
|
+
// baseline was never seen, reconnect the mux to re-fetch it (the host
|
|
2342
|
+
// re-pushes the full snapshot on every fresh mux connection)
|
|
2343
|
+
const snap = this.jobsBySession.get(sessionId);
|
|
2344
|
+
this.jobs = snap ?? [];
|
|
2345
|
+
if (snap !== undefined) {
|
|
2346
|
+
this.chat.running = snap.some((j) => j.status === "running");
|
|
2347
|
+
} else if (this.api.connected && typeof this.api.refreshMux === "function") {
|
|
2348
|
+
this.api.refreshMux();
|
|
2349
|
+
}
|
|
2326
2350
|
await this.chat.open(sessionId);
|
|
2327
2351
|
this.loadFeedback();
|
|
2328
2352
|
this.updateModel();
|