pilotswarm 0.5.32 → 0.5.34
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 +2 -2
- package/tui/src/app.js +10 -0
- package/tui/src/node-sdk-transport.js +11 -0
- package/ui/core/src/controller.js +381 -12
- package/ui/core/src/reducer.js +125 -28
- package/ui/core/src/selectors.js +158 -268
- package/ui/core/src/state.js +6 -1
- package/ui/core/src/themes/catppuccin-mocha.js +2 -1
- package/ui/core/src/themes/cobalt2.js +2 -1
- package/ui/core/src/themes/doom.js +2 -1
- package/ui/core/src/themes/dracula.js +2 -1
- package/ui/core/src/themes/github-dark.js +2 -1
- package/ui/core/src/themes/github-light.js +2 -1
- package/ui/core/src/themes/gruvbox-dark.js +2 -1
- package/ui/core/src/themes/hacker-x-matrix.js +2 -1
- package/ui/core/src/themes/hacker-x-orion-prime.js +2 -1
- package/ui/core/src/themes/helpers.js +22 -4
- package/ui/core/src/themes/high-contrast-mono.js +2 -1
- package/ui/core/src/themes/index.js +24 -5
- package/ui/core/src/themes/ms-dos.js +2 -1
- package/ui/core/src/themes/noctis-obscuro.js +2 -1
- package/ui/core/src/themes/noctis.js +2 -1
- package/ui/core/src/themes/rust.js +125 -0
- package/ui/core/src/themes/spongebob.js +110 -0
- package/ui/core/src/themes/terminal-green.js +2 -1
- package/ui/core/src/themes/win95.js +2 -1
- package/ui/core/src/themes/winamp.js +2 -1
- package/ui/core/src/themes/workspace-dark.js +2 -1
- package/ui/react/src/web-app.js +240 -266
- package/web/dist/assets/{abnfDiagram-VRR7QNED-mXFQ4a3c.js → abnfDiagram-VRR7QNED-BFOimVWK.js} +1 -1
- package/web/dist/assets/{arc-BPuuvbge.js → arc-uUHjvwEo.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-Dt4uUYIe.js → architectureDiagram-ZJ3FMSHR-CxeX_vea.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-Ax8Q22vV.js → blockDiagram-677ZJIJ3-Cno8kltG.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-CPiukyHt.js → c4Diagram-LMCZKHZV-viONVybf.js} +1 -1
- package/web/dist/assets/channel-IURmSsjn.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-VJLqCZJX.js → chunk-2Q5K7J3B-CKfICIGD.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-DqOshxhf.js → chunk-32BRIVSS-CxmwEY56.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-KPqV21jV.js → chunk-5VM5RSS4-_eVYLGll.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-DfhiCVl_.js → chunk-EX3LRPZG-C4aqbjrf.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-H6khJPuW.js → chunk-JWPE2WC7-Bh8ZxrI8.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-BKMAzQxj.js → chunk-MOJQB5TN-BXgLlWVP.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-DNXRTxtj.js → chunk-RYQCIY6F-B1rMIdfF.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-B56PM6Gv.js → chunk-V7JOEXUC-C3AnSHXk.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-Boi3hGkB.js → chunk-VR4S4FIN-CeoMJIBs.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-DmJyn890.js → chunk-XXDRQBXY-DzaxQ9iP.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-FsfpM3iu.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-FsfpM3iu.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-BRdFlFWt.js → cose-bilkent-JH36ORCC-G46fvUOx.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-DqE-xib2.js → cynefin-VYW2F7L2-DXwqVhWA.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-D-QBZjIL.js → cynefinDiagram-TSTJHNR4-DNNFmaXb.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-DYu9oQV6.js → dagre-VKFMJZFB-BCfh-h3o.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-DOwYccCg.js → diagram-FQU43EPY-CyG9b6bj.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-DTSEmrPr.js → diagram-G47NLZAW-DdxDahK3.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-OqbzzquA.js → diagram-NH7WQ7WH-CjrZ_eja.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-BMVG6A8I.js → diagram-OA4YK3LP-5NAjQpk2.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-Rn2Q5xaz.js → diagram-WEI45ONY-fLuDb54-.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-aL32_v5y.js → ebnfDiagram-CCIWWBDH-DL9qnZpa.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-DKCpyczx.js → erDiagram-Q63AITRT-C_ynfrWY.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-CrZTPgR_.js → flowDiagram-23GEKE2U-dYuH8HYv.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-DnSE1OtC.js → ganttDiagram-NO4QXBWP-e8Meh3f3.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-C5SeYPUR.js → gitGraphDiagram-IHSO6WYX-D_tZ4VL9.js} +1 -1
- package/web/dist/assets/index-Ct7SQv1M.css +1 -0
- package/web/dist/assets/{index-B-TEKrRB.js → index-DcQes9h8.js} +6 -6
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-DDnJZp0X.js → infoDiagram-FWYZ7A6U-Dffhqp73.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-ewhzfFkq.js → ishikawaDiagram-FXEZZL3T-DYt9H7kU.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-T0iWWkLM.js → journeyDiagram-5HDEW3XC-Qv1vVu0F.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-D1MFQZjb.js → kanban-definition-HUTT4EX6-CTAUo40G.js} +1 -1
- package/web/dist/assets/{linear-BKfPK9wE.js → linear-CqLsWFLj.js} +1 -1
- package/web/dist/assets/{mermaid.core-CyzD7dTc.js → mermaid.core-okRCGPQQ.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-CWXtcTlP.js → mindmap-definition-LN4V7U3C-DVOOrIwD.js} +1 -1
- package/web/dist/assets/{papaparse.min-DLUoV5k5.js → papaparse.min-Ddflu8oC.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-BFjZun-r.js → pegDiagram-2B236MQR-BLJlC0MZ.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-DMDtW4g7.js → pieDiagram-ENE6RG2P-DdOwICOg.js} +1 -1
- package/web/dist/assets/pilotswarm-CKh2MxfL.js +99 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-CAwPVaYL.js → quadrantDiagram-ABIIQ3AL-B0Th1YO5.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-Co102PkQ.js → railroadDiagram-RFXS5EU6-BF2XKMKf.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-D19VlRSl.js → requirementDiagram-TGXJPOKE-Mr1iSwbS.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-Sd_a3ukL.js → sankeyDiagram-HTMAVEWB-CrOjVZyg.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-CojDRK1c.js → sequenceDiagram-DBY2YBRQ-H0X4Lydk.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-XqXcxnDo.js → sizeCapture-X5ZJPWSS-Dus1QEqh.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-BcN_w699.js → stateDiagram-2N3HPSRC-BOBUSwdR.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DABTDwFh.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-BcO6ai_I.js → swimlanes-5IMT3BWC-BoqT-Lh7.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BF5mFN4t.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-DOPfYxUq.js → timeline-definition-FHXFAJF6-BOrqUQHX.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-BSKLkEOC.js → vennDiagram-L72KCM5P-DDkNL-Oo.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-BdcYAVw2.js → wardleyDiagram-EHGQE667-XUAJQ2X0.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-C6EJT4jB.js → xychartDiagram-FW5EYKEG-ph69PV9y.js} +1 -1
- package/web/dist/index.html +3 -3
- package/ui/core/src/themes/dark-high-contrast.js +0 -56
- package/ui/core/src/themes/workspace-dark-rich.js +0 -65
- package/web/dist/assets/channel-BCzDctYg.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-ChqjVZil.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-ChqjVZil.js +0 -1
- package/web/dist/assets/index-D2B5bzSN.css +0 -1
- package/web/dist/assets/pilotswarm-DA0u7Cq_.js +0 -99
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-BV_w1D3C.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BnYdP0qP.js +0 -8
package/ui/core/src/reducer.js
CHANGED
|
@@ -321,15 +321,19 @@ function mergeDefinedSessionFields(previousSession = {}, nextSession = {}) {
|
|
|
321
321
|
return merged;
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
// Mirrors selectors' getSessionVisualStatus — the two must agree or the row
|
|
325
|
+
// label (debounced, from here) and the summary label (immediate, from there)
|
|
326
|
+
// will disagree mid-turn.
|
|
324
327
|
function computeRawSessionVisualStatus(session) {
|
|
325
328
|
if (!session) return "unknown";
|
|
326
329
|
const status = session.status || "unknown";
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
&& (status === "waiting" || status === "idle" || status === "unknown")
|
|
330
|
-
) {
|
|
330
|
+
const dormant = status === "waiting" || status === "idle" || status === "unknown";
|
|
331
|
+
if (session.cronActive === true && dormant) {
|
|
331
332
|
return "cron_waiting";
|
|
332
333
|
}
|
|
334
|
+
if (dormant && (Number(session.activeChildCount) || 0) > 0) {
|
|
335
|
+
return "awaiting_children";
|
|
336
|
+
}
|
|
333
337
|
return status;
|
|
334
338
|
}
|
|
335
339
|
|
|
@@ -396,6 +400,76 @@ function mergeSessionRowVisualStatus(previousSession, nextSession, nowMs) {
|
|
|
396
400
|
};
|
|
397
401
|
}
|
|
398
402
|
|
|
403
|
+
// A sub-agent that finishes its task stays alive and IDLE waiting for follow-up
|
|
404
|
+
// (see managed-session's spawn_agent contract), so "has children" says nothing
|
|
405
|
+
// about whether work is still happening below. Only these two statuses mean the
|
|
406
|
+
// parent is genuinely blocked on someone else.
|
|
407
|
+
const ACTIVE_DESCENDANT_STATUSES = new Set(["running", "input_required"]);
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Stamp `activeChildCount` — the number of running/input_required descendants,
|
|
411
|
+
* at any depth — onto every session that has one. selectors' visual-status
|
|
412
|
+
* derivation reads it to distinguish "idle because it is waiting on its
|
|
413
|
+
* children" from "idle because there is nothing to do".
|
|
414
|
+
*
|
|
415
|
+
* Counted transitively so the whole ancestor chain lights up: without that, a
|
|
416
|
+
* parent whose direct child is itself only waiting on a grandchild would read
|
|
417
|
+
* as plain idle and the delegation would look dead.
|
|
418
|
+
*
|
|
419
|
+
* Returns `byId` unchanged when nothing moved, so this stays free for the
|
|
420
|
+
* common poll that changes nothing.
|
|
421
|
+
*/
|
|
422
|
+
function applyActiveDescendantCounts(byId) {
|
|
423
|
+
const counts = new Map();
|
|
424
|
+
for (const session of Object.values(byId || {})) {
|
|
425
|
+
if (!session?.sessionId || session.isGroup) continue;
|
|
426
|
+
if (!ACTIVE_DESCENDANT_STATUSES.has(session.status)) continue;
|
|
427
|
+
// A malformed parent chain (self-parent, cycle) would spin forever;
|
|
428
|
+
// `seen` bounds the walk to one visit per ancestor.
|
|
429
|
+
const seen = new Set([session.sessionId]);
|
|
430
|
+
let parentId = session.parentSessionId;
|
|
431
|
+
while (parentId && byId[parentId] && !seen.has(parentId)) {
|
|
432
|
+
seen.add(parentId);
|
|
433
|
+
counts.set(parentId, (counts.get(parentId) || 0) + 1);
|
|
434
|
+
parentId = byId[parentId].parentSessionId;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
let changed = false;
|
|
439
|
+
const next = {};
|
|
440
|
+
for (const [sessionId, session] of Object.entries(byId || {})) {
|
|
441
|
+
const nextCount = counts.get(sessionId) || 0;
|
|
442
|
+
const previousCount = Number(session?.activeChildCount) || 0;
|
|
443
|
+
if (nextCount === previousCount) {
|
|
444
|
+
next[sessionId] = session;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
changed = true;
|
|
448
|
+
next[sessionId] = nextCount > 0
|
|
449
|
+
? { ...session, activeChildCount: nextCount }
|
|
450
|
+
: { ...session, activeChildCount: undefined };
|
|
451
|
+
}
|
|
452
|
+
return changed ? next : byId;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Run the row-status debounce across a whole map. A session going running or
|
|
457
|
+
* idle moves its ANCESTORS' visual status too (they gain or lose
|
|
458
|
+
* awaiting_children), and those ancestors are not in the payload that caused
|
|
459
|
+
* the change — so the debounce has to be re-evaluated for everyone, not just
|
|
460
|
+
* the rows that arrived.
|
|
461
|
+
*/
|
|
462
|
+
function applyRowVisualStatuses(previousById = {}, nextById = {}, nowMs) {
|
|
463
|
+
let changed = false;
|
|
464
|
+
const out = {};
|
|
465
|
+
for (const [sessionId, session] of Object.entries(nextById)) {
|
|
466
|
+
const merged = mergeSessionRowVisualStatus(previousById[sessionId], session, nowMs);
|
|
467
|
+
out[sessionId] = merged;
|
|
468
|
+
if (merged !== session) changed = true;
|
|
469
|
+
}
|
|
470
|
+
return changed ? out : nextById;
|
|
471
|
+
}
|
|
472
|
+
|
|
399
473
|
function normalizedPendingQuestionText(pendingQuestion) {
|
|
400
474
|
return String(pendingQuestion?.question || "").trim();
|
|
401
475
|
}
|
|
@@ -681,6 +755,10 @@ export function appReducer(state, action) {
|
|
|
681
755
|
},
|
|
682
756
|
};
|
|
683
757
|
|
|
758
|
+
case "ui/touchScale":
|
|
759
|
+
if (Boolean(action.enabled) === Boolean(state.ui.touchScale)) return state;
|
|
760
|
+
return { ...state, ui: { ...state.ui, touchScale: Boolean(action.enabled) } };
|
|
761
|
+
|
|
684
762
|
case "profileSettings/apply": {
|
|
685
763
|
const settings = action.settings && typeof action.settings === "object" && !Array.isArray(action.settings)
|
|
686
764
|
? action.settings
|
|
@@ -690,8 +768,10 @@ export function appReducer(state, action) {
|
|
|
690
768
|
&& settings.themeId.trim();
|
|
691
769
|
const hasOwnerFilter = Object.prototype.hasOwnProperty.call(settings, "sessionOwnerFilter");
|
|
692
770
|
const hasLayout = Object.prototype.hasOwnProperty.call(settings, "layoutAdjustments");
|
|
693
|
-
// "rich" retired as a view mode
|
|
694
|
-
//
|
|
771
|
+
// "rich" was retired as a view mode — stored profiles that still
|
|
772
|
+
// say "rich" fall through to the transcript below.
|
|
773
|
+
const hasTouchScale = Object.prototype.hasOwnProperty.call(settings, "touchScale")
|
|
774
|
+
&& typeof settings.touchScale === "boolean";
|
|
695
775
|
const hasChatViewMode = Object.prototype.hasOwnProperty.call(settings, "chatViewMode")
|
|
696
776
|
&& (settings.chatViewMode === "summary" || settings.chatViewMode === "transcript");
|
|
697
777
|
const hasPins = Object.prototype.hasOwnProperty.call(settings, "pinnedSessionIds");
|
|
@@ -755,6 +835,7 @@ export function appReducer(state, action) {
|
|
|
755
835
|
ui: {
|
|
756
836
|
...selection.ui,
|
|
757
837
|
themeId: hasTheme ? settings.themeId.trim() : selection.ui.themeId,
|
|
838
|
+
touchScale: hasTouchScale ? settings.touchScale : selection.ui.touchScale,
|
|
758
839
|
chatViewMode: hasChatViewMode ? settings.chatViewMode : selection.ui.chatViewMode,
|
|
759
840
|
layout: nextLayout,
|
|
760
841
|
},
|
|
@@ -1105,8 +1186,13 @@ export function appReducer(state, action) {
|
|
|
1105
1186
|
const hadRow = Boolean(state.sessions.byId[goneId]);
|
|
1106
1187
|
const wasActive = state.sessions.activeSessionId === goneId;
|
|
1107
1188
|
if (!hadRow && !wasActive) return state;
|
|
1108
|
-
const
|
|
1109
|
-
delete
|
|
1189
|
+
const evictedById = { ...state.sessions.byId };
|
|
1190
|
+
delete evictedById[goneId];
|
|
1191
|
+
// Re-derive: the evicted row may have been the running CHILD that
|
|
1192
|
+
// put an ancestor into awaiting_children, and that ancestor is not
|
|
1193
|
+
// named in this action. Without it the parent reads "waiting on 1"
|
|
1194
|
+
// with nothing left to wait for until the next poll.
|
|
1195
|
+
const byId = applyActiveDescendantCounts(evictedById);
|
|
1110
1196
|
const selectedIds = Array.isArray(state.sessions.selectedIds)
|
|
1111
1197
|
? state.sessions.selectedIds.filter((id) => id !== goneId)
|
|
1112
1198
|
: state.sessions.selectedIds;
|
|
@@ -1134,13 +1220,7 @@ export function appReducer(state, action) {
|
|
|
1134
1220
|
}
|
|
1135
1221
|
for (const session of action.sessions) {
|
|
1136
1222
|
const previous = state.sessions.byId[session.sessionId];
|
|
1137
|
-
|
|
1138
|
-
previous,
|
|
1139
|
-
mergeDefinedSessionFields(previous, session),
|
|
1140
|
-
nowMs,
|
|
1141
|
-
);
|
|
1142
|
-
byId[session.sessionId] = merged;
|
|
1143
|
-
if (!anyChanged && merged !== previous) anyChanged = true;
|
|
1223
|
+
byId[session.sessionId] = mergeDefinedSessionFields(previous, session);
|
|
1144
1224
|
}
|
|
1145
1225
|
if (
|
|
1146
1226
|
state.sessions.activeSessionId
|
|
@@ -1153,9 +1233,17 @@ export function appReducer(state, action) {
|
|
|
1153
1233
|
};
|
|
1154
1234
|
anyChanged = true;
|
|
1155
1235
|
}
|
|
1236
|
+
// Descendant counts BEFORE row visual status: awaiting_children is
|
|
1237
|
+
// derived from activeChildCount, so stamping it after the debounce
|
|
1238
|
+
// ran would leave every parent one poll behind its own children.
|
|
1239
|
+
const countedById = applyActiveDescendantCounts(byId);
|
|
1240
|
+
const rowStatusById = applyRowVisualStatuses(state.sessions.byId, countedById, nowMs);
|
|
1241
|
+
for (const [sessionId, session] of Object.entries(rowStatusById)) {
|
|
1242
|
+
if (session !== state.sessions.byId[sessionId]) { anyChanged = true; break; }
|
|
1243
|
+
}
|
|
1156
1244
|
// Check if session set changed (added/removed)
|
|
1157
1245
|
const prevIds = Object.keys(state.sessions.byId);
|
|
1158
|
-
const nextIds = Object.keys(
|
|
1246
|
+
const nextIds = Object.keys(rowStatusById);
|
|
1159
1247
|
if (prevIds.length !== nextIds.length) anyChanged = true;
|
|
1160
1248
|
if (!anyChanged) {
|
|
1161
1249
|
for (const id of nextIds) {
|
|
@@ -1163,7 +1251,7 @@ export function appReducer(state, action) {
|
|
|
1163
1251
|
}
|
|
1164
1252
|
}
|
|
1165
1253
|
if (!anyChanged) return state;
|
|
1166
|
-
const mergedSessions = Object.values(
|
|
1254
|
+
const mergedSessions = Object.values(rowStatusById);
|
|
1167
1255
|
const hasExistingOrder = Object.keys(state.sessions.orderById || {}).length > 0;
|
|
1168
1256
|
const initialCollapsedIds = collectDefaultCollapsedSessionIds(mergedSessions);
|
|
1169
1257
|
const previousCollapsedIdsExplicit = Boolean(state.sessions.collapsedIdsExplicit);
|
|
@@ -1205,12 +1293,12 @@ export function appReducer(state, action) {
|
|
|
1205
1293
|
// Drop pins/selection for sessions that no longer exist; only
|
|
1206
1294
|
// keep pins on top-level rows (groups and ungrouped top-level
|
|
1207
1295
|
// sessions). Sessions inside containers lose their pins.
|
|
1208
|
-
const survivingPins = prunePinnedIds(state.sessions.pinnedIds,
|
|
1209
|
-
const survivingSelected = pruneIdList(state.sessions.selectedIds,
|
|
1296
|
+
const survivingPins = prunePinnedIds(state.sessions.pinnedIds, rowStatusById);
|
|
1297
|
+
const survivingSelected = pruneIdList(state.sessions.selectedIds, rowStatusById);
|
|
1210
1298
|
const flat = buildSessionTree(mergedSessions, collapsedIds, orderById, survivingPins, state.sessions.manualOrder);
|
|
1211
1299
|
const nextSessions = {
|
|
1212
1300
|
...state.sessions,
|
|
1213
|
-
byId,
|
|
1301
|
+
byId: rowStatusById,
|
|
1214
1302
|
collapsedIds,
|
|
1215
1303
|
collapsedIdsExplicit: previousCollapsedIdsExplicit,
|
|
1216
1304
|
listingSeen: true,
|
|
@@ -1314,16 +1402,25 @@ export function appReducer(state, action) {
|
|
|
1314
1402
|
case "sessions/merged": {
|
|
1315
1403
|
if (!action.session?.sessionId) return state;
|
|
1316
1404
|
const previousSession = state.sessions.byId[action.session.sessionId];
|
|
1317
|
-
const mergedSession =
|
|
1318
|
-
|
|
1319
|
-
|
|
1405
|
+
const mergedSession = mergeDefinedSessionFields(previousSession, action.session);
|
|
1406
|
+
// A single session going running/idle changes its ANCESTORS' counts
|
|
1407
|
+
// and therefore their visual status, so both passes run over the
|
|
1408
|
+
// whole map rather than the one row. buildSessionTree below is
|
|
1409
|
+
// already an O(n) pass over the same map, so this adds no new
|
|
1410
|
+
// complexity class.
|
|
1411
|
+
const byId = applyRowVisualStatuses(
|
|
1412
|
+
state.sessions.byId,
|
|
1413
|
+
applyActiveDescendantCounts({
|
|
1414
|
+
...state.sessions.byId,
|
|
1415
|
+
[action.session.sessionId]: mergedSession,
|
|
1416
|
+
}),
|
|
1320
1417
|
Date.now(),
|
|
1321
1418
|
);
|
|
1322
|
-
|
|
1323
|
-
const byId
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1419
|
+
let anyRowChanged = false;
|
|
1420
|
+
for (const [sessionId, session] of Object.entries(byId)) {
|
|
1421
|
+
if (session !== state.sessions.byId[sessionId]) { anyRowChanged = true; break; }
|
|
1422
|
+
}
|
|
1423
|
+
if (!anyRowChanged) return state;
|
|
1327
1424
|
const survivingPins = prunePinnedIds(state.sessions.pinnedIds, byId);
|
|
1328
1425
|
const {
|
|
1329
1426
|
orderById,
|