pilotswarm 0.5.37 → 0.5.39
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/mcp/dist/src/tools/agent-packages.d.ts.map +1 -1
- package/mcp/dist/src/tools/agent-packages.js +34 -14
- package/mcp/dist/src/tools/agent-packages.js.map +1 -1
- package/package.json +3 -3
- package/tui/src/node-sdk-transport.js +113 -18
- package/ui/core/src/controller.js +196 -49
- package/ui/core/src/layout.js +10 -0
- package/ui/core/src/reducer.js +265 -20
- package/ui/core/src/selectors.js +132 -10
- package/ui/core/src/state.js +90 -2
- package/ui/core/src/themes/index.js +1 -1
- package/ui/react/src/web-app.js +1097 -220
- package/web/auth/config.js +3 -0
- package/web/auth/index.js +8 -1
- package/web/auth/normalize/proxy.js +48 -0
- package/web/auth/providers/proxy.js +141 -0
- package/web/dist/assets/{abnfDiagram-VRR7QNED-DlvPH-8O.js → abnfDiagram-VRR7QNED-Hsly1fgf.js} +1 -1
- package/web/dist/assets/{arc-r1liz5e5.js → arc-DiQLL9vM.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-Bci5dNH3.js → architectureDiagram-ZJ3FMSHR-B8ry8WNZ.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-Bug32AXp.js → blockDiagram-677ZJIJ3-Z4PcukIo.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-DwxSFqgE.js → c4Diagram-LMCZKHZV-Cahkc0gc.js} +1 -1
- package/web/dist/assets/channel-BHLBPE50.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-B_dbI_xg.js → chunk-2Q5K7J3B-CtZuWH4J.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-BkF68W-R.js → chunk-32BRIVSS-C0M390-k.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-BSbNIbJu.js → chunk-5VM5RSS4-ClvX9u5j.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-Blv_Iqa2.js → chunk-EX3LRPZG-BVAet5KG.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-Dk_dBFKd.js → chunk-JWPE2WC7-BccmZmrr.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-DqNFBrfw.js → chunk-MOJQB5TN-BN5MKe94.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-Dlv7Svpb.js → chunk-RYQCIY6F-DfWw-ACP.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-DozGikwu.js → chunk-V7JOEXUC-BgUEoLnN.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-CmkqvGT4.js → chunk-VR4S4FIN-k1eKfFNA.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-DyByai5N.js → chunk-XXDRQBXY-DPyV10yR.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-rj9D2TSL.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-rj9D2TSL.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-Bv-gRVvA.js → cose-bilkent-JH36ORCC-Bt_rpukK.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-DRnAsyZU.js → cynefin-VYW2F7L2-Z8eMzFMv.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-B1fnUtjo.js → cynefinDiagram-TSTJHNR4-BJSNawFi.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-DM6-QENB.js → dagre-VKFMJZFB-D6dNl8jc.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-BPxxnLdU.js → diagram-FQU43EPY-Cy1nbGsU.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-BgtJT3qF.js → diagram-G47NLZAW-CmrbuA_d.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-uekeIzsy.js → diagram-NH7WQ7WH-PoLNQE2j.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-BVy3JZWE.js → diagram-OA4YK3LP-BZwNFcSV.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-BWdjUr32.js → diagram-WEI45ONY-pKw0b_fN.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-DD4fwhNK.js → ebnfDiagram-CCIWWBDH-UNctuQCV.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-BllD_6Kh.js → erDiagram-Q63AITRT-DxIETMif.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-QEYKkAUN.js → flowDiagram-23GEKE2U-C06IdNKV.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-uZcfKTSW.js → ganttDiagram-NO4QXBWP-fU7aGyP1.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-XPdbPaH-.js → gitGraphDiagram-IHSO6WYX-9UKkPJm6.js} +1 -1
- package/web/dist/assets/index-B8nGlHwb.js +24 -0
- package/web/dist/assets/index-DtL0lbis.css +1 -0
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-DXCnQOJs.js → infoDiagram-FWYZ7A6U-C6F06SQJ.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-687_4FBy.js → ishikawaDiagram-FXEZZL3T-CB-O9NfO.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-GXlKuda8.js → journeyDiagram-5HDEW3XC-B-8MOf-H.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-C38-2DvN.js → kanban-definition-HUTT4EX6-DX0XA3hI.js} +1 -1
- package/web/dist/assets/{linear-lGPXiBcz.js → linear-D60PrjTW.js} +1 -1
- package/web/dist/assets/{mermaid.core-C_neEtab.js → mermaid.core-bonzwapf.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-BaJ9tpEG.js → mindmap-definition-LN4V7U3C-Y-6Dum33.js} +1 -1
- package/web/dist/assets/{papaparse.min-rDwKYbBf.js → papaparse.min-Cas1JfRG.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-BdXJLYgY.js → pegDiagram-2B236MQR-_paO0yDw.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-DU19t6fM.js → pieDiagram-ENE6RG2P-xT1_Gpn4.js} +1 -1
- package/web/dist/assets/pilotswarm-BT6tOknQ.js +96 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-CBkOdPp0.js → quadrantDiagram-ABIIQ3AL-1usdLFOF.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-D2DMcGDw.js → railroadDiagram-RFXS5EU6-R3lwHOz9.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-Bx9tF1pr.js → requirementDiagram-TGXJPOKE-BL8J1__Z.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-DcLlRfsi.js → sankeyDiagram-HTMAVEWB-Dz6--0pl.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-xr6UVRBO.js → sequenceDiagram-DBY2YBRQ-CG2Xpkct.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-0fUkSXmg.js → sizeCapture-X5ZJPWSS-BPbYfjxj.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-BWgJGGpg.js → stateDiagram-2N3HPSRC-46x_y_aA.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-C2tnolSo.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-CZKya4_i.js → swimlanes-5IMT3BWC-SXdMo4zJ.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-Dh6Sn__t.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-Df8BpeAu.js → timeline-definition-FHXFAJF6-CGgT-b38.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-ChX5bjT6.js → vennDiagram-L72KCM5P-BGS3xBa_.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-Dr48BgaJ.js → wardleyDiagram-EHGQE667-m2U3wEZJ.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-Cia_MFfc.js → xychartDiagram-FW5EYKEG-DI6tQysg.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/runtime.js +30 -7
- package/web/dist/assets/channel-jVpSYat3.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-C78zSajE.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-C78zSajE.js +0 -1
- package/web/dist/assets/index-BuDSoRIL.css +0 -1
- package/web/dist/assets/index-D1itTxvB.js +0 -24
- package/web/dist/assets/pilotswarm-BNKGG936.js +0 -96
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DmAC8DdB.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-CVYx20_X.js +0 -8
package/ui/core/src/selectors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { INSPECTOR_TABS, FOCUS_REGIONS } from "./commands.js";
|
|
2
|
+
import { canvasKey as canvasSlotKey, parseCanvasKey } from "./state.js";
|
|
2
3
|
import { isManuallyOrderableSession } from "./session-tree.js";
|
|
3
4
|
import {
|
|
4
5
|
createSplashCard,
|
|
@@ -1125,6 +1126,10 @@ export function selectSessionRows(state) {
|
|
|
1125
1126
|
// built under one branding to a caller using another, and the root
|
|
1126
1127
|
// session's name would flip between them.
|
|
1127
1128
|
state.branding?.title,
|
|
1129
|
+
// The canvas marker below reads these. Without them the memo hands
|
|
1130
|
+
// back a row built before the draw and the marker never appears.
|
|
1131
|
+
state.canvas?.bySessionId?.[entry.sessionId],
|
|
1132
|
+
state.canvas?.prefs?.[entry.sessionId],
|
|
1128
1133
|
];
|
|
1129
1134
|
const cached = memo.get(entry.sessionId);
|
|
1130
1135
|
if (cached && sameRowDeps(cached.deps, deps)) return cached.row;
|
|
@@ -1160,6 +1165,15 @@ export function selectSessionRows(state) {
|
|
|
1160
1165
|
: null,
|
|
1161
1166
|
orderable: isManuallyOrderableSession(session),
|
|
1162
1167
|
orderContainer: manualOrderContainerKey(session, pinned),
|
|
1168
|
+
// Canvas state for THIS row. The toolbar badge answers the same
|
|
1169
|
+
// question for the active session only; the list has to answer it
|
|
1170
|
+
// for every session at once, which is the point — a canvas drawn
|
|
1171
|
+
// in a session you are not looking at is otherwise invisible.
|
|
1172
|
+
//
|
|
1173
|
+
// null no canvas, or one that was cleared
|
|
1174
|
+
// "canvas" a canvas exists and nothing is new
|
|
1175
|
+
// "unseen" it changed since this user last looked at it
|
|
1176
|
+
canvasMark: sessionCanvasMark(state, entry.sessionId),
|
|
1163
1177
|
};
|
|
1164
1178
|
memo.set(entry.sessionId, { deps, row });
|
|
1165
1179
|
return row;
|
|
@@ -2661,21 +2675,87 @@ export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
|
2661
2675
|
* whether the yellow unseen-changes badge should light
|
|
2662
2676
|
* (latestRev > lastViewedRev, and only while the canvas is not on screen).
|
|
2663
2677
|
*/
|
|
2678
|
+
/**
|
|
2679
|
+
* The canvas marker for one session row.
|
|
2680
|
+
*
|
|
2681
|
+
* Same rules selectCanvasView applies to the active session, minus the
|
|
2682
|
+
* on-screen check: a row's marker describes the SESSION, not the layout, so it
|
|
2683
|
+
* does not care which column happens to be open.
|
|
2684
|
+
*
|
|
2685
|
+
* A cleared canvas (draw_canvas("") -> sizeBytes 0) no longer exists. Unknown
|
|
2686
|
+
* size (null, from a degraded snapshot) is presumed drawn — a marker that is
|
|
2687
|
+
* occasionally early beats one that silently never shows.
|
|
2688
|
+
*/
|
|
2689
|
+
export function sessionCanvasMark(state, sessionId) {
|
|
2690
|
+
if (!sessionId) return null;
|
|
2691
|
+
// Aggregate across the session's slots: ANY unseen slot lights the dot;
|
|
2692
|
+
// any drawn slot at all marks the row. Slot 1 is the bare id key.
|
|
2693
|
+
let mark = null;
|
|
2694
|
+
for (let slot = 1; slot <= 5; slot += 1) {
|
|
2695
|
+
const key = canvasSlotKey(sessionId, slot);
|
|
2696
|
+
const entry = state?.canvas?.bySessionId?.[key];
|
|
2697
|
+
const prefs = state?.canvas?.prefs?.[key];
|
|
2698
|
+
if (!entry) {
|
|
2699
|
+
// No live state loaded (cold page). canvasPrefs persist per user,
|
|
2700
|
+
// so a slot this user has ever VIEWED still marks — plain, never
|
|
2701
|
+
// "unseen": whether it changed since is unknowable client-side.
|
|
2702
|
+
if ((prefs?.lastViewedRev || 0) > 0) mark = mark || "canvas";
|
|
2703
|
+
continue;
|
|
2704
|
+
}
|
|
2705
|
+
const latestRev = entry.latestRev || 0;
|
|
2706
|
+
if (latestRev <= 0 || entry.sizeBytes === 0) continue;
|
|
2707
|
+
const unseen = latestRev > (prefs?.lastViewedRev || 0)
|
|
2708
|
+
|| (entry.latestDataRev || 0) > (prefs?.lastViewedDataRev || 0);
|
|
2709
|
+
if (unseen) return "unseen";
|
|
2710
|
+
mark = "canvas";
|
|
2711
|
+
}
|
|
2712
|
+
return mark;
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2664
2715
|
export function selectCanvasView(state) {
|
|
2665
2716
|
const sessionId = state?.sessions?.activeSessionId || null;
|
|
2666
|
-
|
|
2667
|
-
|
|
2717
|
+
// The pane shows ONE slot at a time. An out-of-range or never-drawn slot
|
|
2718
|
+
// falls back to 1 so a stale selection can never strand the pane.
|
|
2719
|
+
const wantedSlot = Number(state?.ui?.canvasSlot) || 1;
|
|
2720
|
+
const bySessionId = state?.canvas?.bySessionId || {};
|
|
2721
|
+
// Every drawn slot of the active session, for the selector and arrows.
|
|
2722
|
+
const slots = [];
|
|
2723
|
+
if (sessionId) {
|
|
2724
|
+
for (let slot = 1; slot <= 5; slot += 1) {
|
|
2725
|
+
const e = bySessionId[canvasSlotKey(sessionId, slot)];
|
|
2726
|
+
if (!e || (e.latestRev || 0) <= 0 || e.sizeBytes === 0) continue;
|
|
2727
|
+
const p = state?.canvas?.prefs?.[canvasSlotKey(sessionId, slot)];
|
|
2728
|
+
slots.push({
|
|
2729
|
+
slot,
|
|
2730
|
+
name: typeof e.name === "string" ? e.name : "",
|
|
2731
|
+
latestRev: e.latestRev || 0,
|
|
2732
|
+
unseen: (e.latestRev || 0) > (p?.lastViewedRev || 0)
|
|
2733
|
+
|| (e.latestDataRev || 0) > (p?.lastViewedDataRev || 0),
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
const slot = slots.some((s2) => s2.slot === wantedSlot) ? wantedSlot : 1;
|
|
2738
|
+
const key = sessionId ? canvasSlotKey(sessionId, slot) : null;
|
|
2739
|
+
const entry = key ? bySessionId[key] : null;
|
|
2740
|
+
const prefs = key ? state?.canvas?.prefs?.[key] : null;
|
|
2668
2741
|
const latestRev = entry?.latestRev || 0;
|
|
2669
2742
|
const lastViewedRev = prefs?.lastViewedRev || 0;
|
|
2670
2743
|
const latestDataRev = entry?.latestDataRev || 0;
|
|
2671
2744
|
const lastViewedDataRev = prefs?.lastViewedDataRev || 0;
|
|
2672
|
-
|
|
2745
|
+
// Read from canvasOpen, not the retired rightPaneMode enum. Diagnostics
|
|
2746
|
+
// moving to its own column means "not canvas" no longer implies "panes",
|
|
2747
|
+
// and this drives the unseen badge, which must light whenever the canvas
|
|
2748
|
+
// is off screen.
|
|
2749
|
+
const mode = state?.ui?.canvasOpen ? "canvas" : "panes";
|
|
2673
2750
|
// A cleared canvas (draw_canvas("") → sizeBytes 0) no longer exists for
|
|
2674
2751
|
// affordance purposes: no phone tab, no badge, blank teaching state.
|
|
2675
2752
|
// Unknown size (null — snapshot degrade) is presumed drawn.
|
|
2676
2753
|
const exists = latestRev > 0 && entry?.sizeBytes !== 0;
|
|
2677
2754
|
return {
|
|
2678
2755
|
sessionId,
|
|
2756
|
+
slot,
|
|
2757
|
+
slots,
|
|
2758
|
+
name: typeof entry?.name === "string" ? entry.name : "",
|
|
2679
2759
|
mode,
|
|
2680
2760
|
exists,
|
|
2681
2761
|
latestRev,
|
|
@@ -3644,9 +3724,29 @@ export function selectAdminConsole(state) {
|
|
|
3644
3724
|
distinctPackageOwners.add(key);
|
|
3645
3725
|
}
|
|
3646
3726
|
const decoratePackageOwners = distinctPackageOwners.size > 1;
|
|
3727
|
+
// Copy-identity helpers: one NAME can be two packages (scope shadowing),
|
|
3728
|
+
// so row selection and highlighting key on (name, scope, owner) — never
|
|
3729
|
+
// the bare name.
|
|
3730
|
+
const packageCopyKey = (name, scope, owner) =>
|
|
3731
|
+
`${name}\u0001${scope === "shared" ? "shared" : "user"}\u0001${scope === "shared" ? "" : (owner?.subject ?? "")}`;
|
|
3732
|
+
const selectedCopyKey = pkgState.selectedName
|
|
3733
|
+
? packageCopyKey(
|
|
3734
|
+
pkgState.selectedName,
|
|
3735
|
+
pkgState.selectedSelector?.scope ?? null,
|
|
3736
|
+
pkgState.selectedSelector?.owner ?? principal ?? null,
|
|
3737
|
+
)
|
|
3738
|
+
: null;
|
|
3739
|
+
const rowMatchesSelection = (pkg) => {
|
|
3740
|
+
if (admin.section !== "packages" || pkgState.selectedName !== pkg.name) return false;
|
|
3741
|
+
// Legacy selection with no selector: fall back to name-only so an
|
|
3742
|
+
// in-flight selection from an older host still highlights something.
|
|
3743
|
+
if (!pkgState.selectedSelector?.scope) return true;
|
|
3744
|
+
return packageCopyKey(pkg.name, pkg.scope, pkg.owner) === selectedCopyKey;
|
|
3745
|
+
};
|
|
3647
3746
|
const packageRow = (pkg) => ({
|
|
3648
3747
|
name: pkg.name,
|
|
3649
3748
|
scope: pkg.scope === "shared" ? "shared" : "user",
|
|
3749
|
+
owner: pkg.owner ?? null,
|
|
3650
3750
|
enabled: Boolean(pkg.enabled),
|
|
3651
3751
|
semver: pkg.active?.semver || null,
|
|
3652
3752
|
sha7: pkg.active?.sha256 ? String(pkg.active.sha256).slice(0, 7) : null,
|
|
@@ -3662,7 +3762,7 @@ export function selectAdminConsole(state) {
|
|
|
3662
3762
|
: ownerBadgeFor(resolvePackageOwner(pkg, ownerDirectory)),
|
|
3663
3763
|
// Highlight only while the Agents section is active — otherwise the
|
|
3664
3764
|
// GitHub Keys screen shows a double selection.
|
|
3665
|
-
selected:
|
|
3765
|
+
selected: rowMatchesSelection(pkg),
|
|
3666
3766
|
});
|
|
3667
3767
|
// Admins are served every user-scope package, but another person's private
|
|
3668
3768
|
// agents are not part of YOUR workspace — keep them out of the main tree
|
|
@@ -3707,13 +3807,13 @@ export function selectAdminConsole(state) {
|
|
|
3707
3807
|
{ id: "ghcp", kind: "section", depth: 0, label: "GitHub Keys", selected: section === "ghcp" },
|
|
3708
3808
|
{ id: "agents", kind: "section", depth: 0, label: "Agents", selected: section === "packages" && !pkgState.selectedName },
|
|
3709
3809
|
{ id: "group:shared", kind: "group", depth: 1, label: "Shared", count: sharedRows.length },
|
|
3710
|
-
...sharedRows.map((row) => ({ id: `pkg:${row.name}`, kind: "package", depth: 2, label: row.name, ...row })),
|
|
3810
|
+
...sharedRows.map((row) => ({ id: `pkg:shared:${row.name}`, kind: "package", depth: 2, label: row.name, ...row })),
|
|
3711
3811
|
{ id: "group:user", kind: "group", depth: 1, label: "User", count: userRows.length },
|
|
3712
|
-
...userRows.map((row) => ({ id: `pkg:${row.name}`, kind: "package", depth: 2, label: row.name, ...row })),
|
|
3812
|
+
...userRows.map((row) => ({ id: `pkg:user:${row.name}`, kind: "package", depth: 2, label: row.name, ...row })),
|
|
3713
3813
|
...(otherUserRows.length > 0
|
|
3714
3814
|
? [
|
|
3715
3815
|
{ id: "group:others", kind: "group", depth: 1, label: "Other users", count: otherUserRows.length },
|
|
3716
|
-
...otherUserRows.map((row) => ({ id: `pkg:${row.name}`, kind: "package", depth: 2, label: row.name, ...row })),
|
|
3816
|
+
...otherUserRows.map((row) => ({ id: `pkg:user:${row.owner?.subject ?? "?"}:${row.name}`, kind: "package", depth: 2, label: row.name, ...row })),
|
|
3717
3817
|
]
|
|
3718
3818
|
: []),
|
|
3719
3819
|
// The worker registry is a hard-gated admin read; hide the section
|
|
@@ -3725,7 +3825,10 @@ export function selectAdminConsole(state) {
|
|
|
3725
3825
|
const detail = pkgState.detail || null;
|
|
3726
3826
|
let packageDetail = null;
|
|
3727
3827
|
if (pkgState.selectedName) {
|
|
3728
|
-
const
|
|
3828
|
+
const sameName = pkgList.filter((pkg) => pkg.name === pkgState.selectedName);
|
|
3829
|
+
const summary = (pkgState.selectedSelector?.scope
|
|
3830
|
+
? sameName.find((pkg) => packageCopyKey(pkg.name, pkg.scope, pkg.owner) === selectedCopyKey)
|
|
3831
|
+
: sameName[0]) ?? sameName[0] ?? null;
|
|
3729
3832
|
const activeVersion = detail?.versions?.find((v) => v.versionId === detail.activeVersionId) || null;
|
|
3730
3833
|
const manifest = activeVersion?.manifest || summary?.active?.manifest || {};
|
|
3731
3834
|
// Workers are ephemeral pods: rows for retired pod names linger until
|
|
@@ -3739,9 +3842,22 @@ export function selectAdminConsole(state) {
|
|
|
3739
3842
|
return !Number.isNaN(at.getTime()) && at.getTime() >= liveCutoff;
|
|
3740
3843
|
});
|
|
3741
3844
|
const fleetTotal = workerRows.length;
|
|
3845
|
+
// Workers report installed state by bare name — except when two
|
|
3846
|
+
// same-named copies are installed at once, where each row gets a
|
|
3847
|
+
// qualified key (name#scope[:owner8]) so the collision stays visible.
|
|
3848
|
+
// Read whichever key this copy answers to.
|
|
3849
|
+
const installedRowFor = (worker) => {
|
|
3850
|
+
const installed = worker.installed || {};
|
|
3851
|
+
const scope = detail?.scope || summary?.scope || pkgState.selectedSelector?.scope || null;
|
|
3852
|
+
const ownerSubject = (detail?.owner?.subject || summary?.owner?.subject || pkgState.selectedSelector?.owner?.subject || "");
|
|
3853
|
+
const qualified = scope === "user"
|
|
3854
|
+
? `${pkgState.selectedName}#user${ownerSubject ? `:${ownerSubject.slice(0, 8)}` : ""}`
|
|
3855
|
+
: `${pkgState.selectedName}#shared`;
|
|
3856
|
+
return installed[qualified] ?? installed[pkgState.selectedName];
|
|
3857
|
+
};
|
|
3742
3858
|
const fleetCurrent = activeVersion
|
|
3743
|
-
? workerRows.filter((worker) => worker
|
|
3744
|
-
&& worker
|
|
3859
|
+
? workerRows.filter((worker) => installedRowFor(worker)?.semver === activeVersion.semver
|
|
3860
|
+
&& installedRowFor(worker)?.status === "ok").length
|
|
3745
3861
|
: 0;
|
|
3746
3862
|
const canManage = detail ? ownsPackage(detail) : (summary ? ownsPackage(summary) : false);
|
|
3747
3863
|
packageDetail = {
|
|
@@ -3776,6 +3892,12 @@ export function selectAdminConsole(state) {
|
|
|
3776
3892
|
: null,
|
|
3777
3893
|
actionPending: pkgState.action?.pending || null,
|
|
3778
3894
|
actionError: pkgState.action?.error || null,
|
|
3895
|
+
// Which same-named copies exist, for the republish affordance and
|
|
3896
|
+
// the shadowing note. Counterparts the caller can SEE only —
|
|
3897
|
+
// exactly what the list already enforces.
|
|
3898
|
+
hasSharedCounterpart: sameName.some((pkg) => pkg.scope === "shared"),
|
|
3899
|
+
hasOwnUserCounterpart: sameName.some((pkg) => pkg.scope !== "shared" && isMinePackage(pkg)),
|
|
3900
|
+
copyCount: sameName.length,
|
|
3779
3901
|
};
|
|
3780
3902
|
}
|
|
3781
3903
|
|
package/ui/core/src/state.js
CHANGED
|
@@ -73,11 +73,23 @@ export function normalizeStoredLayoutAdjustments(layoutAdjustments) {
|
|
|
73
73
|
const sessionPaneAdjust = Number(candidate.sessionPaneAdjust);
|
|
74
74
|
const portalSessionColumnAdjust = Number(candidate.portalSessionColumnAdjust);
|
|
75
75
|
const activityPaneAdjust = Number(candidate.activityPaneAdjust);
|
|
76
|
+
// Canvas is a column of its own now rather than a face the right column
|
|
77
|
+
// wears, so it needs its own remembered width. Same mechanism as the rest:
|
|
78
|
+
// a pixel delta from the computed base, persisted per user.
|
|
79
|
+
const canvasPaneAdjust = Number(candidate.canvasPaneAdjust);
|
|
80
|
+
const diagnosticsPaneAdjust = Number(candidate.diagnosticsPaneAdjust);
|
|
81
|
+
const diagnosticsSplitAdjust = Number(candidate.diagnosticsSplitAdjust);
|
|
76
82
|
return {
|
|
77
83
|
paneAdjust: Number.isFinite(paneAdjust) ? Math.trunc(paneAdjust) : 0,
|
|
78
84
|
sessionPaneAdjust: Number.isFinite(sessionPaneAdjust) ? Math.trunc(sessionPaneAdjust) : 0,
|
|
79
85
|
portalSessionColumnAdjust: Number.isFinite(portalSessionColumnAdjust) ? Math.trunc(portalSessionColumnAdjust) : 0,
|
|
80
86
|
activityPaneAdjust: Number.isFinite(activityPaneAdjust) ? Math.trunc(activityPaneAdjust) : 0,
|
|
87
|
+
canvasPaneAdjust: Number.isFinite(canvasPaneAdjust) ? Math.trunc(canvasPaneAdjust) : 0,
|
|
88
|
+
diagnosticsPaneAdjust: Number.isFinite(diagnosticsPaneAdjust) ? Math.trunc(diagnosticsPaneAdjust) : 0,
|
|
89
|
+
// The inspector/activity seam inside Diagnostics, in PIXELS from an
|
|
90
|
+
// even split. The old row-quantized TUI adjust made browser drags
|
|
91
|
+
// jump in ~20px notches.
|
|
92
|
+
diagnosticsSplitAdjust: Number.isFinite(diagnosticsSplitAdjust) ? Math.trunc(diagnosticsSplitAdjust) : 0,
|
|
81
93
|
};
|
|
82
94
|
}
|
|
83
95
|
|
|
@@ -132,11 +144,66 @@ export function normalizeStoredCollapsedSessionIds(value) {
|
|
|
132
144
|
* Right-column mode: the inspector/activity split, or the session canvas.
|
|
133
145
|
* Anything unrecognized falls back to panes — a stored value from a newer
|
|
134
146
|
* build must not strand an older portal in a mode it cannot render.
|
|
147
|
+
*
|
|
148
|
+
* @deprecated Superseded by the independent `canvasOpen` / `diagnosticsOpen`
|
|
149
|
+
* booleans. Kept only to migrate a value written by an older build — see
|
|
150
|
+
* normalizeStoredDesktopPanes.
|
|
135
151
|
*/
|
|
136
152
|
export function normalizeStoredRightPaneMode(value) {
|
|
137
153
|
return value === "canvas" ? "canvas" : "panes";
|
|
138
154
|
}
|
|
139
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Which of the two optional desktop columns are on screen.
|
|
158
|
+
*
|
|
159
|
+
* Canvas and Diagnostics used to be one enum (`rightPaneMode`), so the right
|
|
160
|
+
* column could only ever wear one face. They are independent now: either, both,
|
|
161
|
+
* or neither. Diagnostics is the old inspector/activity split, moved out of the
|
|
162
|
+
* chat pane and travelling as one unit.
|
|
163
|
+
*
|
|
164
|
+
* Both default to OFF. A pane that is off has no width and no resizer — it is
|
|
165
|
+
* absent, not collapsed to nothing.
|
|
166
|
+
*
|
|
167
|
+
* Migrating a stored `rightPaneMode`:
|
|
168
|
+
* "canvas" -> canvas on. That was a deliberate choice, so it is honored.
|
|
169
|
+
* "panes" -> both off. "panes" was the default value rather than a choice,
|
|
170
|
+
* and the new default is a clean two-column workspace.
|
|
171
|
+
*/
|
|
172
|
+
export function normalizeStoredDesktopPanes(value, legacyRightPaneMode) {
|
|
173
|
+
const candidate = value && typeof value === "object" ? value : null;
|
|
174
|
+
if (candidate && (typeof candidate.canvasOpen === "boolean" || typeof candidate.diagnosticsOpen === "boolean")) {
|
|
175
|
+
return {
|
|
176
|
+
canvasOpen: candidate.canvasOpen === true,
|
|
177
|
+
diagnosticsOpen: candidate.diagnosticsOpen === true,
|
|
178
|
+
// Zen: chat as a narrow rail, canvas as the workbench. Only
|
|
179
|
+
// meaningful with the canvas open.
|
|
180
|
+
zen: candidate.zen === true && candidate.canvasOpen === true,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
canvasOpen: legacyRightPaneMode === "canvas",
|
|
185
|
+
diagnosticsOpen: false,
|
|
186
|
+
zen: false,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The store key for one canvas surface. Slot 1 keeps the bare session id so
|
|
192
|
+
* every reader written before slots existed keeps working untouched; slots
|
|
193
|
+
* 2-5 append "#<slot>". Used for canvas.bySessionId AND canvas.prefs.
|
|
194
|
+
*/
|
|
195
|
+
export function canvasKey(sessionId, slot) {
|
|
196
|
+
const id = String(sessionId || "");
|
|
197
|
+
const n = Number(slot);
|
|
198
|
+
return Number.isInteger(n) && n >= 2 && n <= 5 ? `${id}#${n}` : id;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The inverse: `sessionId#3` -> { sessionId, slot: 3 }; bare id -> slot 1. */
|
|
202
|
+
export function parseCanvasKey(key) {
|
|
203
|
+
const m = /^(.*)#([2-5])$/.exec(String(key || ""));
|
|
204
|
+
return m ? { sessionId: m[1], slot: Number(m[2]) } : { sessionId: String(key || ""), slot: 1 };
|
|
205
|
+
}
|
|
206
|
+
|
|
140
207
|
/**
|
|
141
208
|
* Per-session canvas preferences: what this user has chosen and seen.
|
|
142
209
|
*
|
|
@@ -154,10 +221,13 @@ export function normalizeStoredCanvasPrefs(value) {
|
|
|
154
221
|
if (!id || !entry || typeof entry !== "object") continue;
|
|
155
222
|
const lastViewedRev = Number(entry.lastViewedRev);
|
|
156
223
|
const lastViewedDataRev = Number(entry.lastViewedDataRev);
|
|
224
|
+
const zenRailPx = Number(entry.zenRailPx);
|
|
157
225
|
out[id] = {
|
|
158
226
|
optedOut: entry.optedOut === true,
|
|
159
227
|
lastViewedRev: Number.isFinite(lastViewedRev) && lastViewedRev > 0 ? Math.floor(lastViewedRev) : 0,
|
|
160
228
|
lastViewedDataRev: Number.isFinite(lastViewedDataRev) && lastViewedDataRev > 0 ? Math.floor(lastViewedDataRev) : 0,
|
|
229
|
+
// Zen chat-rail width, remembered for THIS session only.
|
|
230
|
+
...(Number.isFinite(zenRailPx) && zenRailPx > 0 ? { zenRailPx: Math.floor(zenRailPx) } : {}),
|
|
161
231
|
};
|
|
162
232
|
}
|
|
163
233
|
return out;
|
|
@@ -169,7 +239,11 @@ export function normalizeStoredActiveSessionId(value) {
|
|
|
169
239
|
return id ? id : null;
|
|
170
240
|
}
|
|
171
241
|
|
|
172
|
-
export function createInitialState({ mode = "local", branding = null, docs = null, themeId = null, sessionOwnerFilter = null, layoutAdjustments = null, pinnedSessionIds = null, collapsedSessionIds = null, activeSessionId = null, sessionOrder = null, rightPaneMode = null, canvasPrefs = null } = {}) {
|
|
242
|
+
export function createInitialState({ mode = "local", branding = null, docs = null, themeId = null, sessionOwnerFilter = null, layoutAdjustments = null, pinnedSessionIds = null, collapsedSessionIds = null, activeSessionId = null, sessionOrder = null, rightPaneMode = null, desktopPanes: storedDesktopPanes = null, canvasPrefs = null } = {}) {
|
|
243
|
+
const desktopPanes = normalizeStoredDesktopPanes(
|
|
244
|
+
storedDesktopPanes,
|
|
245
|
+
normalizeStoredRightPaneMode(rightPaneMode),
|
|
246
|
+
);
|
|
173
247
|
const hasStoredSessionOwnerFilter = sessionOwnerFilter != null;
|
|
174
248
|
const hasStoredCollapsedSessionIds = collapsedSessionIds != null;
|
|
175
249
|
const initialLayoutAdjustments = normalizeStoredLayoutAdjustments(layoutAdjustments);
|
|
@@ -201,7 +275,21 @@ export function createInitialState({ mode = "local", branding = null, docs = nul
|
|
|
201
275
|
// through to the transcript.
|
|
202
276
|
// Which face the right column wears: the inspector/activity panes
|
|
203
277
|
// or the session canvas. Persisted.
|
|
204
|
-
|
|
278
|
+
//
|
|
279
|
+
// @deprecated Read by nothing that decides layout any more — the
|
|
280
|
+
// two booleans below do that. Still derived so a build that rolls
|
|
281
|
+
// back, and anything still reading the old key, sees a sane value.
|
|
282
|
+
rightPaneMode: desktopPanes.canvasOpen ? "canvas" : "panes",
|
|
283
|
+
// The two optional desktop columns, independent of each other.
|
|
284
|
+
// Both off is the default workspace: sessions and chat only.
|
|
285
|
+
canvasOpen: desktopPanes.canvasOpen,
|
|
286
|
+
diagnosticsOpen: desktopPanes.diagnosticsOpen,
|
|
287
|
+
// Chat-rail + canvas-workbench mode. Persisted with the columns.
|
|
288
|
+
canvasZen: desktopPanes.zen,
|
|
289
|
+
// Canvas filling the workspace. Deliberately NOT persisted: it is
|
|
290
|
+
// a "look at this now" gesture, and returning to a portal with no
|
|
291
|
+
// chat and no session list would read as a broken app.
|
|
292
|
+
canvasMaximized: false,
|
|
205
293
|
statsViewMode: "session",
|
|
206
294
|
prompt: "",
|
|
207
295
|
promptCursor: 0,
|
|
@@ -61,7 +61,7 @@ const THEMES = Object.freeze([
|
|
|
61
61
|
|
|
62
62
|
const THEME_MAP = new Map(THEMES.map((theme) => [theme.id, theme]));
|
|
63
63
|
|
|
64
|
-
export const DEFAULT_THEME_ID =
|
|
64
|
+
export const DEFAULT_THEME_ID = workspaceDarkTheme.id;
|
|
65
65
|
|
|
66
66
|
export { THEME_GROUP_ORDER } from "./helpers.js";
|
|
67
67
|
|