pilotswarm 0.5.70 → 0.5.71

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.
Files changed (76) hide show
  1. package/package.json +2 -2
  2. package/ui/core/src/controller.js +3 -1
  3. package/ui/core/src/moa.js +2 -2
  4. package/ui/core/src/reducer.js +14 -2
  5. package/ui/core/src/selectors.js +3 -3
  6. package/ui/core/src/session-sort.js +45 -0
  7. package/ui/core/src/state.js +3 -0
  8. package/ui/react/src/web-app.js +48 -12
  9. package/web/dist/assets/{abnfDiagram-VRR7QNED-CidJDJrY.js → abnfDiagram-VRR7QNED-C1caNr4U.js} +1 -1
  10. package/web/dist/assets/{arc-BDO1TDjs.js → arc-BufdFJYK.js} +1 -1
  11. package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-BIcgz3Aa.js → architectureDiagram-ZJ3FMSHR-C0vQblUK.js} +1 -1
  12. package/web/dist/assets/{blockDiagram-677ZJIJ3-CWtdYLva.js → blockDiagram-677ZJIJ3-B6PYWI9u.js} +1 -1
  13. package/web/dist/assets/{c4Diagram-LMCZKHZV-u_IOLAS7.js → c4Diagram-LMCZKHZV-DFoaVeMm.js} +1 -1
  14. package/web/dist/assets/channel-CDzPwSI9.js +1 -0
  15. package/web/dist/assets/{chunk-2Q5K7J3B-daWW1UxL.js → chunk-2Q5K7J3B-CxFBb-8y.js} +1 -1
  16. package/web/dist/assets/{chunk-32BRIVSS-DyxnM56K.js → chunk-32BRIVSS-DO1G8AVc.js} +1 -1
  17. package/web/dist/assets/{chunk-5VM5RSS4-D7taC4Nx.js → chunk-5VM5RSS4-spE4bKSd.js} +1 -1
  18. package/web/dist/assets/{chunk-EX3LRPZG-BWo08pDw.js → chunk-EX3LRPZG-Bc_eUP5F.js} +1 -1
  19. package/web/dist/assets/{chunk-JWPE2WC7-AhysI3TD.js → chunk-JWPE2WC7-CGM2GAMX.js} +1 -1
  20. package/web/dist/assets/{chunk-MOJQB5TN-CDyGPb8L.js → chunk-MOJQB5TN-CHdrVT1z.js} +1 -1
  21. package/web/dist/assets/{chunk-RYQCIY6F-RhKa7iem.js → chunk-RYQCIY6F-BWJK7-AW.js} +1 -1
  22. package/web/dist/assets/{chunk-V7JOEXUC-DLgws19l.js → chunk-V7JOEXUC-D8tgpQwK.js} +1 -1
  23. package/web/dist/assets/{chunk-VR4S4FIN-CyLmmpZk.js → chunk-VR4S4FIN-BP86JfVu.js} +1 -1
  24. package/web/dist/assets/{chunk-XXDRQBXY-DYN0KnEU.js → chunk-XXDRQBXY-Ctq_LUrs.js} +1 -1
  25. package/web/dist/assets/classDiagram-OUVF2IWQ-DdcABn-U.js +1 -0
  26. package/web/dist/assets/classDiagram-v2-EOCWNBFH-DdcABn-U.js +1 -0
  27. package/web/dist/assets/{cose-bilkent-JH36ORCC-62MpINYU.js → cose-bilkent-JH36ORCC-DNvC4thT.js} +1 -1
  28. package/web/dist/assets/{cynefin-VYW2F7L2-CHVeQsGv.js → cynefin-VYW2F7L2-3zVj2GvI.js} +1 -1
  29. package/web/dist/assets/{cynefinDiagram-TSTJHNR4-BYgzn_3A.js → cynefinDiagram-TSTJHNR4-DQbITfsJ.js} +1 -1
  30. package/web/dist/assets/{dagre-VKFMJZFB-C3qJqyHj.js → dagre-VKFMJZFB-BkGAcDyj.js} +1 -1
  31. package/web/dist/assets/{diagram-FQU43EPY-BBYvv2Hx.js → diagram-FQU43EPY-D3lsgelm.js} +1 -1
  32. package/web/dist/assets/{diagram-G47NLZAW-1Ca1QwqV.js → diagram-G47NLZAW-C2rO9e3d.js} +1 -1
  33. package/web/dist/assets/{diagram-NH7WQ7WH-CicNpuah.js → diagram-NH7WQ7WH-DJBwtT8q.js} +1 -1
  34. package/web/dist/assets/{diagram-OA4YK3LP-BDGMGAu8.js → diagram-OA4YK3LP-C-SAwEHZ.js} +1 -1
  35. package/web/dist/assets/{diagram-WEI45ONY-EgaBHcZC.js → diagram-WEI45ONY-BpXMlmKz.js} +1 -1
  36. package/web/dist/assets/{ebnfDiagram-CCIWWBDH-BEGQ-CpA.js → ebnfDiagram-CCIWWBDH-DwxUgEmE.js} +1 -1
  37. package/web/dist/assets/{erDiagram-Q63AITRT-BifquFiz.js → erDiagram-Q63AITRT-DNZMCmYz.js} +1 -1
  38. package/web/dist/assets/{flowDiagram-23GEKE2U-BOEV0nau.js → flowDiagram-23GEKE2U-CUlGtnWV.js} +1 -1
  39. package/web/dist/assets/{ganttDiagram-NO4QXBWP-Die6C1uw.js → ganttDiagram-NO4QXBWP-DCj6zkyt.js} +1 -1
  40. package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-BS6qOLxN.js → gitGraphDiagram-IHSO6WYX-Q6UP_dgi.js} +1 -1
  41. package/web/dist/assets/index-23y0f1it.js +24 -0
  42. package/web/dist/assets/index-BXR-II2L.css +1 -0
  43. package/web/dist/assets/{infoDiagram-FWYZ7A6U-C3dgkkDc.js → infoDiagram-FWYZ7A6U-Cwn8XNQT.js} +1 -1
  44. package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-BmGRDlL-.js → ishikawaDiagram-FXEZZL3T-BaMvGBAo.js} +1 -1
  45. package/web/dist/assets/{journeyDiagram-5HDEW3XC-De0y-2Pp.js → journeyDiagram-5HDEW3XC-DK95FJ0t.js} +1 -1
  46. package/web/dist/assets/{kanban-definition-HUTT4EX6-CPVl9fbH.js → kanban-definition-HUTT4EX6-C6W6RJLm.js} +1 -1
  47. package/web/dist/assets/{linear-BwcW14FN.js → linear-Un37Oyxr.js} +1 -1
  48. package/web/dist/assets/{mermaid.core-BPC8y2lr.js → mermaid.core-i8uqvLPa.js} +4 -4
  49. package/web/dist/assets/{mindmap-definition-LN4V7U3C-CmTjYZ6-.js → mindmap-definition-LN4V7U3C-d_cgq1KW.js} +1 -1
  50. package/web/dist/assets/{papaparse.min-DwUBIiJJ.js → papaparse.min-BiJEkQ8f.js} +1 -1
  51. package/web/dist/assets/{pegDiagram-2B236MQR-DI77hWU9.js → pegDiagram-2B236MQR-5QRWM5AP.js} +1 -1
  52. package/web/dist/assets/{pieDiagram-ENE6RG2P-CQL62GJk.js → pieDiagram-ENE6RG2P-VIZXsjFV.js} +1 -1
  53. package/web/dist/assets/pilotswarm-D7Q48waO.js +109 -0
  54. package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-CQoKIYFZ.js → quadrantDiagram-ABIIQ3AL-DMOx841b.js} +1 -1
  55. package/web/dist/assets/{railroadDiagram-RFXS5EU6-B2KkUNRk.js → railroadDiagram-RFXS5EU6-lTWzjdcd.js} +1 -1
  56. package/web/dist/assets/{requirementDiagram-TGXJPOKE-Dgl152x0.js → requirementDiagram-TGXJPOKE-BE5SpiNy.js} +1 -1
  57. package/web/dist/assets/{sankeyDiagram-HTMAVEWB-B-SjWvvt.js → sankeyDiagram-HTMAVEWB-C3LYWQTA.js} +1 -1
  58. package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-CuuRcXdJ.js → sequenceDiagram-DBY2YBRQ-BTCp88tQ.js} +1 -1
  59. package/web/dist/assets/{sizeCapture-X5ZJPWSS-TzEpZfA7.js → sizeCapture-X5ZJPWSS-Ppzj8n5l.js} +1 -1
  60. package/web/dist/assets/{stateDiagram-2N3HPSRC-XAi55Ttk.js → stateDiagram-2N3HPSRC-Ch_iZ_cQ.js} +1 -1
  61. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-BbJN7t8_.js +1 -0
  62. package/web/dist/assets/{swimlanes-5IMT3BWC-C6ATJwDO.js → swimlanes-5IMT3BWC-BX4-Oeby.js} +2 -2
  63. package/web/dist/assets/swimlanesDiagram-G3AALYLV-0UA1VebA.js +8 -0
  64. package/web/dist/assets/{timeline-definition-FHXFAJF6-DhUjhdEH.js → timeline-definition-FHXFAJF6-B1l-6l6u.js} +1 -1
  65. package/web/dist/assets/{vennDiagram-L72KCM5P-CxoeZ7Ri.js → vennDiagram-L72KCM5P-Grpa6wFZ.js} +1 -1
  66. package/web/dist/assets/{wardleyDiagram-EHGQE667-ChgUaxkB.js → wardleyDiagram-EHGQE667-B5bXnFlL.js} +1 -1
  67. package/web/dist/assets/{xychartDiagram-FW5EYKEG-iV0ACrP1.js → xychartDiagram-FW5EYKEG-BkELQtgc.js} +1 -1
  68. package/web/dist/index.html +3 -3
  69. package/web/dist/assets/channel-KxoOVq0K.js +0 -1
  70. package/web/dist/assets/classDiagram-OUVF2IWQ-B6yEKJ21.js +0 -1
  71. package/web/dist/assets/classDiagram-v2-EOCWNBFH-B6yEKJ21.js +0 -1
  72. package/web/dist/assets/index-CCniDa-9.css +0 -1
  73. package/web/dist/assets/index-DpkfY3jc.js +0 -24
  74. package/web/dist/assets/pilotswarm-BnfVgmru.js +0 -109
  75. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CXVciPM_.js +0 -1
  76. package/web/dist/assets/swimlanesDiagram-G3AALYLV-Dk-VTEgD.js +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pilotswarm",
3
- "version": "0.5.70",
3
+ "version": "0.5.71",
4
4
  "description": "PilotSwarm application package: terminal UI, browser portal + Web API server, and MCP server — one install, three bins.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -81,7 +81,7 @@
81
81
  "ink": "^6.8.0",
82
82
  "jose": "^6.2.2",
83
83
  "mermaid": "^11.16.0",
84
- "pilotswarm-sdk": "0.5.70",
84
+ "pilotswarm-sdk": "0.5.71",
85
85
  "react": "^19.2.4",
86
86
  "react-dom": "^19.2.4",
87
87
  "ws": "^8.18.2"
@@ -7545,6 +7545,7 @@ export class PilotSwarmUiController {
7545
7545
  force = true,
7546
7546
  fullscreen = false,
7547
7547
  pane = false,
7548
+ preserveSession = false,
7548
7549
  } = {}) {
7549
7550
  const resolvedSessionId = sessionId || this.getState().sessions.activeSessionId;
7550
7551
  const name = String(filename || "").trim();
@@ -7558,7 +7559,7 @@ export class PilotSwarmUiController {
7558
7559
  restoreArtifactId: this.getState().files.selectedArtifactId || null,
7559
7560
  });
7560
7561
 
7561
- if (this.getState().sessions.activeSessionId !== resolvedSessionId) {
7562
+ if (!preserveSession && this.getState().sessions.activeSessionId !== resolvedSessionId) {
7562
7563
  await this.loadSession(resolvedSessionId).catch(() => null);
7563
7564
  }
7564
7565
  // The reader REPLACES the inspector, so it has no business changing
@@ -8996,6 +8997,7 @@ export class PilotSwarmUiController {
8996
8997
  return;
8997
8998
  }
8998
8999
 
9000
+ this.dispatch({ type: "sessions/used", sessionId });
8999
9001
  const activePendingQuestion = activeSession?.pendingQuestion || null;
9000
9002
  const answeringPendingQuestion = Boolean(activePendingQuestion?.question);
9001
9003
  const promptEdit = this.getPromptEditSessionMatch(sessionId);
@@ -42,7 +42,7 @@ export function normalizeMoa(value) {
42
42
  return safeLayout(dashboard, key, `MoA ${index + 1}`);
43
43
  });
44
44
  if (!dashboards.length) dashboards.push(safeLayout(null));
45
- return { version: 3, activeDashboardId: dashboards.some(d => d.id === value.activeDashboardId) ? value.activeDashboardId : dashboards[0].id, dashboards };
45
+ return { version: 3, composerMode: value?.composerMode === "shared" ? "shared" : "per-chat", activeDashboardId: dashboards.some(d => d.id === value.activeDashboardId) ? value.activeDashboardId : dashboards[0].id, dashboards };
46
46
  }
47
47
  let source = value;
48
48
  if (!(value?.version === 2 || (object(value) && Object.hasOwn(value, "tree")))) {
@@ -51,7 +51,7 @@ export function normalizeMoa(value) {
51
51
  const active = Number.isInteger(value?.activeSlot) ? Math.max(0, Math.min(4, value.activeSlot)) : 0;
52
52
  source = safeLayout(slots[active]).tree ? slots[active] : slots.find(slot => safeLayout(slot).tree);
53
53
  }
54
- return { version: 3, activeDashboardId: "moa-1", dashboards: [safeLayout(source)] };
54
+ return { version: 3, composerMode: value?.composerMode === "shared" ? "shared" : "per-chat", activeDashboardId: "moa-1", dashboards: [safeLayout(source)] };
55
55
  }
56
56
  export const activeMoaDashboard = value => value.dashboards.find(d => d.id === value.activeDashboardId) || value.dashboards[0];
57
57
  export function updateMoaDashboard(value, dashboardId, patch) {
@@ -1,3 +1,4 @@
1
+ import { normalizeSessionSortMode, normalizeSessionUsage, reconcileSessionSort } from "./session-sort.js";
1
2
  import { normalizeMoa } from "./moa.js";
2
3
  import { retainSessionWarnings } from "./session-errors.js";
3
4
  import { buildSessionTree, isManuallyOrderableSession } from "./session-tree.js";
@@ -846,7 +847,7 @@ export function appReducer(state, action) {
846
847
  if (sessionId && contentUpdate && state.sessions?.goneIds?.includes(sessionId)) return state;
847
848
  const next = baseReducer(state, action);
848
849
  if (next === state) return next;
849
- return reconcileSessionView(state, next, action);
850
+ return reconcileSessionView(state, reconcileSessionSort(state, next, action), action);
850
851
  }
851
852
 
852
853
  // Per-session desktop views, kept in step with the ui slice.
@@ -1101,6 +1102,7 @@ function baseReducer(state, action) {
1101
1102
  const nextActiveSessionId = hasActive
1102
1103
  ? normalizeStoredActiveSessionId(settings.activeSessionId)
1103
1104
  : state.sessions.activeSessionId;
1105
+ const remoteUsage = normalizeSessionUsage(settings.sessionUsedAt);
1104
1106
  const nextSessions = {
1105
1107
  ...state.sessions,
1106
1108
  ...(hasOwnerFilter
@@ -1117,6 +1119,9 @@ function baseReducer(state, action) {
1117
1119
  : {}),
1118
1120
  pinnedIds: nextPinnedIds,
1119
1121
  manualOrder: nextManualOrder,
1122
+ sortMode: Object.hasOwn(settings, "sessionSortMode") ? normalizeSessionSortMode(settings.sessionSortMode) : state.sessions.sortMode,
1123
+ usedAt: Object.fromEntries([...new Set([...Object.keys(remoteUsage), ...Object.keys(state.sessions.usedAt || {})])]
1124
+ .map(id => [id, Math.max(remoteUsage[id] || 0, state.sessions.usedAt?.[id] || 0)])),
1120
1125
  collapsedIds: nextCollapsedIds,
1121
1126
  collapsedIdsExplicit: hasCollapsed ? true : state.sessions.collapsedIdsExplicit,
1122
1127
  activeSessionId: nextActiveSessionId,
@@ -1891,8 +1896,15 @@ function baseReducer(state, action) {
1891
1896
  };
1892
1897
  }
1893
1898
 
1899
+ case "sessions/sortMode":
1900
+ return { ...state, sessions: { ...state.sessions, sortMode: normalizeSessionSortMode(action.mode) } };
1901
+ case "sessions/refreshSort":
1902
+ return { ...state, sessions: { ...state.sessions } };
1903
+ case "sessions/used":
1904
+ if (!action.sessionId) return state;
1905
+ return { ...state, sessions: { ...state.sessions, usedAt: { ...state.sessions.usedAt, [action.sessionId]: action.at || Date.now() } } };
1894
1906
  case "sessions/selected": {
1895
- state = { ...state, sessions: { ...state.sessions, listDeselected: false, localSelectionMade: true } };
1907
+ state = { ...state, sessions: { ...state.sessions, listDeselected: false, localSelectionMade: true, usedAt: action.sessionId ? { ...state.sessions.usedAt, [action.sessionId]: Date.now() } : state.sessions.usedAt } };
1896
1908
  // Per-session chat scroll memory: stash both the outgoing offset
1897
1909
  // and whether it follows the bottom. A paused reading position
1898
1910
  // stays paused as new messages arrive and across session switches.
@@ -1332,7 +1332,7 @@ function sessionSearchOwner(session, byId) {
1332
1332
 
1333
1333
  function sessionSearchIndex(state) {
1334
1334
  const byId = state.sessions?.byId || {};
1335
- const deps = [state.sessions?.orderById, state.sessions?.pinnedIds, state.sessions?.manualOrder];
1335
+ const deps = [state.sessions?.orderById, state.sessions?.pinnedIds, (state.sessions?.sortMode && state.sessions.sortMode !== "saved" ? state.sessions.sortSnapshot : state.sessions?.manualOrder)];
1336
1336
  const cached = sessionSearchIndexCache.get(byId);
1337
1337
  if (cached && sameRowDeps(cached.deps, deps)) return cached;
1338
1338
  const expanded = buildSessionTree(
@@ -1340,7 +1340,7 @@ function sessionSearchIndex(state) {
1340
1340
  new Set(),
1341
1341
  state.sessions?.orderById,
1342
1342
  state.sessions?.pinnedIds,
1343
- state.sessions?.manualOrder,
1343
+ (state.sessions?.sortMode && state.sessions.sortMode !== "saved" ? state.sessions.sortSnapshot : state.sessions?.manualOrder),
1344
1344
  );
1345
1345
  const parentById = new Map();
1346
1346
  const entryById = new Map();
@@ -1371,7 +1371,7 @@ function buildSearchedSessionFlat(state, query) {
1371
1371
  if (!query) return existingFlat;
1372
1372
  const cached = searchedSessionFlatCache.get(existingFlat);
1373
1373
  const deps = [state.sessions?.byId, state.sessions?.ownerFilter, state.auth, state.sessions?.orderById,
1374
- state.sessions?.pinnedIds, state.sessions?.manualOrder, state.sessions?.filterExceptionId];
1374
+ state.sessions?.pinnedIds, (state.sessions?.sortMode && state.sessions.sortMode !== "saved" ? state.sessions.sortSnapshot : state.sessions?.manualOrder), state.sessions?.filterExceptionId];
1375
1375
  if (cached?.query === query && sameRowDeps(cached.deps, deps)) return cached.result;
1376
1376
 
1377
1377
  const byId = state.sessions?.byId || {};
@@ -0,0 +1,45 @@
1
+ import { buildSessionTree } from "./session-tree.js";
2
+
3
+ export const normalizeSessionSortMode = mode => ["used", "updated"].includes(mode) ? mode : "saved";
4
+ export function normalizeSessionUsage(value) {
5
+ return Object.fromEntries(Object.entries(value || {}).filter(([id, at]) => id && Number.isFinite(at) && at > 0));
6
+ }
7
+ export function snapshotSessionOrder(sessions, mode) {
8
+ const saved = buildSessionTree(Object.values(sessions.byId), new Set(), sessions.orderById, sessions.pinnedIds, sessions.manualOrder);
9
+ const rank = new Map(saved.map((row, index) => [row.sessionId, index]));
10
+ const timestamp = session => {
11
+ if (mode === "used") return sessions.usedAt?.[session.sessionId] || 0;
12
+ const raw = session.updatedAt ?? session.summaryUpdatedAt ?? session.latestSummaryUpdatedAt;
13
+ const value = typeof raw === "number" ? raw : Date.parse(raw || "");
14
+ return Number.isFinite(value) ? value : 0;
15
+ };
16
+ return Object.values(sessions.byId).sort((a, b) => timestamp(b) - timestamp(a)
17
+ || (rank.get(a.sessionId) ?? Infinity) - (rank.get(b.sessionId) ?? Infinity))
18
+ .map(session => session.sessionId);
19
+ }
20
+
21
+ // Live catalog and usage changes update row contents, never existing positions.
22
+ // A mode change or explicit refresh captures the next order. New rows append.
23
+ export function reconcileSessionSort(previous, next, action) {
24
+ const sessions = next.sessions;
25
+ if (!sessions) return next;
26
+ const mode = normalizeSessionSortMode(sessions.sortMode);
27
+ const modeChanged = mode !== normalizeSessionSortMode(previous.sessions?.sortMode);
28
+ const refresh = action.type === "sessions/refreshSort";
29
+ const rebuild = sessions.flat !== previous.sessions?.flat || sessions.byId !== previous.sessions?.byId || modeChanged || refresh;
30
+ if (!rebuild) return next;
31
+ let sortSnapshot = sessions.sortSnapshot;
32
+ if (mode !== "saved") {
33
+ if (!sessions.listingSeen) sortSnapshot = [];
34
+ else if (modeChanged || refresh || !sortSnapshot?.length) sortSnapshot = snapshotSessionOrder(sessions, mode);
35
+ else {
36
+ const known = new Set(sortSnapshot);
37
+ const added = Object.keys(sessions.byId).filter(id => !known.has(id));
38
+ if (added.length) sortSnapshot = [...sortSnapshot, ...added];
39
+ }
40
+ }
41
+ const flat = mode === "saved" && !modeChanged ? sessions.flat : buildSessionTree(
42
+ Object.values(sessions.byId), sessions.collapsedIds, sessions.orderById,
43
+ sessions.pinnedIds, mode === "saved" ? sessions.manualOrder : sortSnapshot);
44
+ return { ...next, sessions: { ...sessions, sortSnapshot, flat } };
45
+ }
@@ -480,6 +480,9 @@ export function createInitialState({ mode = "local", branding = null, docs = nul
480
480
  pinnedIds: normalizeStoredPinnedSessionIds(pinnedSessionIds),
481
481
  // Explicit user placement (desktop drag); read by every surface.
482
482
  manualOrder: normalizeStoredSessionOrder(sessionOrder),
483
+ sortMode: "saved",
484
+ sortSnapshot: [],
485
+ usedAt: {},
483
486
  // Whether a listing of each kind has been processed. "This row is
484
487
  // new" is only answerable against a previous listing OF THE SAME
485
488
  // KIND — inferring it from byId being non-empty was wrong, because
@@ -1,3 +1,4 @@
1
+ import { normalizeSessionSortMode, normalizeSessionUsage } from "../../core/src/session-sort.js";
1
2
  import { ChatCallLine } from "./chat-call-line.js";
2
3
  import React from "react";
3
4
  import { FeatureFlagsPanel } from "./feature-flags-panel.js";
@@ -291,6 +292,8 @@ function clearBrowserPreferenceCache() {
291
292
  function normalizeProfileSettings(settings) {
292
293
  const candidate = settings && typeof settings === "object" && !Array.isArray(settings) ? settings : {};
293
294
  const normalized = {};
295
+ if (hasOwn(candidate, "sessionSortMode")) normalized.sessionSortMode = normalizeSessionSortMode(candidate.sessionSortMode);
296
+ if (hasOwn(candidate, "sessionUsedAt")) normalized.sessionUsedAt = normalizeSessionUsage(candidate.sessionUsedAt);
294
297
  if (hasOwn(candidate, "moa")) normalized.moa = normalizeMoa(candidate.moa);
295
298
  if (typeof candidate.themeId === "string" && candidate.themeId.trim()) {
296
299
  normalized.themeId = candidate.themeId.trim();
@@ -441,7 +444,7 @@ function profileViewState(root) {
441
444
  return {
442
445
  ...root.ui, ...root.ui.layout,
443
446
  ownerFilter: root.sessions.ownerFilter, pinnedIds: root.sessions.pinnedIds,
444
- manualOrder: root.sessions.manualOrder, collapsedSessionIds: root.sessions.collapsedIds,
447
+ manualOrder: root.sessions.manualOrder, sessionSortMode: root.sessions.sortMode, sessionUsedAt: root.sessions.usedAt, collapsedSessionIds: root.sessions.collapsedIds,
445
448
  activeSessionId: root.sessions.activeSessionId, canvasPrefs: root.canvas?.prefs,
446
449
  };
447
450
  }
@@ -464,6 +467,8 @@ function profileSettingsFromViewState(state, preservedOtherTouchScale = null, pr
464
467
  },
465
468
  pinnedSessionIds: state.pinnedIds,
466
469
  sessionOrder: state.manualOrder,
470
+ sessionSortMode: state.sessionSortMode,
471
+ sessionUsedAt: state.sessionUsedAt,
467
472
  collapsedSessionIds: state.collapsedSessionIds,
468
473
  activeSessionId: state.activeSessionId,
469
474
  // rightPaneMode describes the DESKTOP right column; a phone has no
@@ -528,6 +533,8 @@ function buildDefaultProfileSettingsFromState(state, preservedOtherTouchScale =
528
533
  layoutAdjustments: state?.ui?.layout,
529
534
  pinnedSessionIds: state?.sessions?.pinnedIds,
530
535
  sessionOrder: state?.sessions?.manualOrder,
536
+ sessionSortMode: state?.sessions?.sortMode,
537
+ sessionUsedAt: state?.sessions?.usedAt,
531
538
  collapsedSessionIds: state?.sessions?.collapsedIds,
532
539
  activeSessionId: state?.sessions?.activeSessionId,
533
540
  ...(isNarrowViewport()
@@ -575,6 +582,8 @@ function materializeProfileSettings(remoteSettings, defaults) {
575
582
  // here is DROPPED at startup — and the save effect then writes the
576
583
  // empty value straight back over the stored one. Leaving sessionOrder
577
584
  // out made every placement survive exactly until the next reload.
585
+ sessionSortMode: normalizedRemote.sessionSortMode ?? normalizedDefaults.sessionSortMode,
586
+ sessionUsedAt: normalizedRemote.sessionUsedAt ?? normalizedDefaults.sessionUsedAt,
578
587
  sessionOrder: hasOwn(normalizedRemote, "sessionOrder")
579
588
  ? normalizedRemote.sessionOrder
580
589
  : normalizedDefaults.sessionOrder,
@@ -2030,6 +2039,10 @@ function ArtifactLink({ artifactRef }) {
2030
2039
  */
2031
2040
  function openArtifactFromChat(controller, sessionId, filename) {
2032
2041
  if (!controller?.revealArtifact) return;
2042
+ if (controller.openChatArtifact) {
2043
+ Promise.resolve(controller.openChatArtifact(sessionId, filename)).catch(() => {});
2044
+ return;
2045
+ }
2033
2046
  const isPhone = typeof window !== "undefined"
2034
2047
  && typeof window.matchMedia === "function"
2035
2048
  && window.matchMedia("(max-width: 920px)").matches;
@@ -5159,6 +5172,8 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
5159
5172
  ownerFilter: state.sessions.ownerFilter,
5160
5173
  pinnedIds: state.sessions.pinnedIds,
5161
5174
  manualOrder: state.sessions.manualOrder,
5175
+ sortMode: state.sessions.sortMode,
5176
+ sortSnapshot: state.sessions.sortSnapshot,
5162
5177
  selectedIds: state.sessions.selectedIds,
5163
5178
  selectMode: state.sessions.selectMode,
5164
5179
  auth: state.auth,
@@ -5198,6 +5213,8 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
5198
5213
  ownerFilter: viewState.ownerFilter,
5199
5214
  pinnedIds: viewState.pinnedIds,
5200
5215
  manualOrder: viewState.manualOrder,
5216
+ sortMode: viewState.sortMode,
5217
+ sortSnapshot: viewState.sortSnapshot,
5201
5218
  selectedIds: viewState.selectedIds,
5202
5219
  selectMode: viewState.selectMode,
5203
5220
  listDeselected: viewState.listDeselected,
@@ -5216,7 +5233,7 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
5216
5233
  prefs: viewState.canvasPrefs,
5217
5234
  },
5218
5235
  budget: { paused: viewState.budgetPaused },
5219
- }), [viewState.activeSessionId, viewState.auth, viewState.branding, viewState.budgetPaused, viewState.canvasBySessionId, viewState.canvasPrefs, viewState.connectionMode, viewState.filterQuery, viewState.listDeselected, viewState.ownerFilter, viewState.pinnedIds, viewState.manualOrder, viewState.selectedIds, viewState.selectMode, viewState.sessionsById, viewState.sessionsFlat]);
5236
+ }), [viewState.activeSessionId, viewState.auth, viewState.branding, viewState.budgetPaused, viewState.canvasBySessionId, viewState.canvasPrefs, viewState.connectionMode, viewState.filterQuery, viewState.listDeselected, viewState.ownerFilter, viewState.pinnedIds, viewState.manualOrder, viewState.sortMode, viewState.sortSnapshot, viewState.selectedIds, viewState.selectMode, viewState.sessionsById, viewState.sessionsFlat]);
5220
5237
  // Hold the previous rows when a poll produced identical output.
5221
5238
  const rows = useStableValue(computedRows);
5222
5239
  const searchScrollRef = React.useRef({ top: 0, left: 0, restore: false });
@@ -5991,15 +6008,31 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
5991
6008
  setRef: setSessionButtonRef,
5992
6009
  // Drag-to-folder is a fine-pointer gesture: arming it on
5993
6010
  // touch hijacks the finger that should be scrolling the list.
5994
- drag: selection || touchInput ? null : dragHandlers,
6011
+ drag: selection || touchInput || viewState.sortMode !== "saved" ? null : dragHandlers,
5995
6012
  }))),
5996
- React.createElement(SessionSearchControl, {
5997
- query: viewState.filterQuery,
5998
- onQuery: setSearchQuery,
5999
- matchCount: searchMatchCount,
6000
- mobile: isMobilePane,
6001
- listRef: sessionListRef,
6002
- }),
6013
+ React.createElement("div", { className: "ps-session-find-controls" },
6014
+ React.createElement("div", { className: "ps-session-sort-controls" },
6015
+ React.createElement("div", { className: "ps-session-sort-modes", role: "group", "aria-label": "Session sort order" },
6016
+ ...[["used", "Recently used", "M12 8v4l3 2", true], ["updated", "Recently updated", "M3 12h4l3-7 4 14 3-7h4"], ["saved", "Saved order", "M6 3h12v18l-6-4-6 4Z"]].map(([mode, label, path, clock]) => React.createElement(IconButton, {
6017
+ key: mode, label, className: "ps-session-sort-button", active: (viewState.sortMode || "saved") === mode, pressed: (viewState.sortMode || "saved") === mode,
6018
+ icon: React.createElement("svg", { viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true }, clock ? React.createElement("circle", { cx: 12, cy: 12, r: 9 }) : null, React.createElement("path", { d: path })),
6019
+ onClick: () => controller.dispatch({ type: "sessions/sortMode", mode }),
6020
+ }))),
6021
+ React.createElement(IconButton, {
6022
+ label: "Refresh session order", className: "ps-session-sort-refresh",
6023
+ icon: React.createElement("svg", { viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true }, React.createElement("path", { d: "M20 7v5h-5M20 12a8 8 0 1 0-2 5M20 7v5" })),
6024
+ onClick: async () => {
6025
+ try { await controller.refreshSessions(); controller.dispatch({ type: "sessions/refreshSort" }); }
6026
+ catch { controller.dispatch({ type: "ui/status", text: "Could not refresh sessions. Try again." }); }
6027
+ },
6028
+ })),
6029
+ React.createElement(SessionSearchControl, {
6030
+ query: viewState.filterQuery,
6031
+ onQuery: setSearchQuery,
6032
+ matchCount: searchMatchCount,
6033
+ mobile: isMobilePane,
6034
+ listRef: sessionListRef,
6035
+ })),
6003
6036
  (showDetailBox === null ? !isMobilePane : showDetailBox)
6004
6037
  ? React.createElement(SessionDetailBox, {
6005
6038
  session: activeSession,
@@ -9381,7 +9414,7 @@ function StatusStrip({ controller }) {
9381
9414
  // (hold ~450ms to see the label, release to dismiss — the long-press does not
9382
9415
  // fire onClick). aria-label carries the meaning for assistive tech.
9383
9416
  const ICON_HOVER_TOOLTIP_MS = 1000;
9384
- function IconButton({ icon, label, onClick, disabled = false, active = false, className = "ps-toolbar-button" }) {
9417
+ function IconButton({ icon, label, onClick, disabled = false, active = false, pressed = undefined, className = "ps-toolbar-button" }) {
9385
9418
  // The tooltip is portaled to <body> so it escapes the toolbar/pane
9386
9419
  // overflow-clipping and stacking contexts (nested tooltips were hidden
9387
9420
  // behind, or bled through by, the panes). Coordinates are computed from
@@ -9524,6 +9557,7 @@ function IconButton({ icon, label, onClick, disabled = false, active = false, cl
9524
9557
  onClick: handleClick,
9525
9558
  disabled,
9526
9559
  "aria-label": label,
9560
+ "aria-pressed": pressed,
9527
9561
  onPointerEnter: startHover,
9528
9562
  onPointerLeave: endHover,
9529
9563
  onPointerDown: startPress,
@@ -14666,6 +14700,8 @@ export function PilotSwarmWebApp({ controller, suspended = false, moa = null })
14666
14700
  // never persists — the reducer would reorder the list correctly and
14667
14701
  // the placement would vanish on reload.
14668
14702
  manualOrder: rootState.sessions.manualOrder,
14703
+ sessionSortMode: rootState.sessions.sortMode,
14704
+ sessionUsedAt: rootState.sessions.usedAt,
14669
14705
  // Pass the live Set reference here so shallow-equal sees the same
14670
14706
  // identity across renders (a fresh array would break memoization).
14671
14707
  // Conversion to a sorted array happens inside `normalizeProfileSettings`.
@@ -15007,7 +15043,7 @@ export function PilotSwarmWebApp({ controller, suspended = false, moa = null })
15007
15043
  });
15008
15044
  }, 400);
15009
15045
  return undefined;
15010
- }, [controller, state.moa, state.activeSessionId, state.activityPaneAdjust, state.agentPickerUsage, state.canvasOpen, state.canvasPaneAdjust, state.canvasPrefs, state.canvasZen, state.collapsedSessionIds, state.diagnosticsOpen, state.diagnosticsPaneAdjust, state.diagnosticsSplitAdjust, state.ownerFilter, state.paneAdjust, state.manualOrder, state.pinnedIds, state.portalSessionColumnAdjust, state.rightPaneMode, state.sessionDetailCollapsed, state.sessionPaneAdjust, state.sessionViews, state.themeId, state.touchScale]);
15046
+ }, [controller, state.moa, state.activeSessionId, state.activityPaneAdjust, state.agentPickerUsage, state.canvasOpen, state.canvasPaneAdjust, state.canvasPrefs, state.canvasZen, state.collapsedSessionIds, state.diagnosticsOpen, state.diagnosticsPaneAdjust, state.diagnosticsSplitAdjust, state.ownerFilter, state.paneAdjust, state.manualOrder, state.sessionSortMode, state.sessionUsedAt, state.pinnedIds, state.portalSessionColumnAdjust, state.rightPaneMode, state.sessionDetailCollapsed, state.sessionPaneAdjust, state.sessionViews, state.themeId, state.touchScale]);
15011
15047
 
15012
15048
  React.useEffect(() => {
15013
15049
  applyDocumentTheme(state.themeId);
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-CDyGPb8L.js";import{p as f}from"./chunk-JWPE2WC7-AhysI3TD.js";import{_ as n,l as o}from"./mermaid.core-BPC8y2lr.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CHVeQsGv.js";import"./pilotswarm-BnfVgmru.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-CHdrVT1z.js";import{p as f}from"./chunk-JWPE2WC7-CGM2GAMX.js";import{_ as n,l as o}from"./mermaid.core-i8uqvLPa.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-3zVj2GvI.js";import"./pilotswarm-D7Q48waO.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-BPC8y2lr.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-i8uqvLPa.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};