pilotswarm 0.5.62 → 0.5.63

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 (74) hide show
  1. package/package.json +2 -2
  2. package/ui/core/src/controller.js +54 -14
  3. package/ui/core/src/moa.js +9 -0
  4. package/ui/core/src/reducer.js +13 -6
  5. package/ui/core/src/state.js +6 -2
  6. package/ui/react/src/components.js +2 -1
  7. package/ui/react/src/web-app.js +73 -23
  8. package/web/dist/assets/{abnfDiagram-VRR7QNED-DAiG41W-.js → abnfDiagram-VRR7QNED-C8iAmIqt.js} +1 -1
  9. package/web/dist/assets/{arc-CN_GY3DG.js → arc-Cg8qs17j.js} +1 -1
  10. package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-DD9qiBOj.js → architectureDiagram-ZJ3FMSHR-fYr0dRm3.js} +1 -1
  11. package/web/dist/assets/{blockDiagram-677ZJIJ3-CZg2R8i6.js → blockDiagram-677ZJIJ3-Bpj79Dzq.js} +1 -1
  12. package/web/dist/assets/{c4Diagram-LMCZKHZV-DuZxUZYd.js → c4Diagram-LMCZKHZV-b26Lsm6m.js} +1 -1
  13. package/web/dist/assets/channel-C-ASbcRr.js +1 -0
  14. package/web/dist/assets/{chunk-2Q5K7J3B-BX7lDvmf.js → chunk-2Q5K7J3B-VLYQfjgG.js} +1 -1
  15. package/web/dist/assets/{chunk-32BRIVSS-COupwzAw.js → chunk-32BRIVSS-mVx--2Hg.js} +1 -1
  16. package/web/dist/assets/{chunk-5VM5RSS4-C_IyERib.js → chunk-5VM5RSS4-jwnJmked.js} +1 -1
  17. package/web/dist/assets/{chunk-EX3LRPZG-CyfIuYKn.js → chunk-EX3LRPZG-DdfzAT1n.js} +1 -1
  18. package/web/dist/assets/{chunk-JWPE2WC7-Cj5lvEfN.js → chunk-JWPE2WC7-BHm6Udd8.js} +1 -1
  19. package/web/dist/assets/{chunk-MOJQB5TN-l907LpMA.js → chunk-MOJQB5TN-Dn5qSVAS.js} +1 -1
  20. package/web/dist/assets/{chunk-RYQCIY6F-PLru1fOZ.js → chunk-RYQCIY6F-D4IKMROn.js} +1 -1
  21. package/web/dist/assets/{chunk-V7JOEXUC-C6GCxiE5.js → chunk-V7JOEXUC-BhME3pRf.js} +1 -1
  22. package/web/dist/assets/{chunk-VR4S4FIN-BGelrIxQ.js → chunk-VR4S4FIN-CHG_NBff.js} +1 -1
  23. package/web/dist/assets/{chunk-XXDRQBXY-BmJstjEv.js → chunk-XXDRQBXY-yw4QCgiL.js} +1 -1
  24. package/web/dist/assets/classDiagram-OUVF2IWQ-D2vlJIb1.js +1 -0
  25. package/web/dist/assets/classDiagram-v2-EOCWNBFH-D2vlJIb1.js +1 -0
  26. package/web/dist/assets/{cose-bilkent-JH36ORCC-CRabxOOK.js → cose-bilkent-JH36ORCC-DqwPj9Kx.js} +1 -1
  27. package/web/dist/assets/{cynefin-VYW2F7L2-CminxBoG.js → cynefin-VYW2F7L2-CVokaaE_.js} +1 -1
  28. package/web/dist/assets/{cynefinDiagram-TSTJHNR4-yBazsQf4.js → cynefinDiagram-TSTJHNR4-Bkhj9fuY.js} +1 -1
  29. package/web/dist/assets/{dagre-VKFMJZFB-DcZuZV2n.js → dagre-VKFMJZFB-DVv-jnjb.js} +1 -1
  30. package/web/dist/assets/{diagram-FQU43EPY-CHoFzsw6.js → diagram-FQU43EPY-DDlZjkrF.js} +1 -1
  31. package/web/dist/assets/{diagram-G47NLZAW-B8leqMRl.js → diagram-G47NLZAW-CNDUgqYa.js} +1 -1
  32. package/web/dist/assets/{diagram-NH7WQ7WH-DNoVxpa3.js → diagram-NH7WQ7WH-DywF8u8i.js} +1 -1
  33. package/web/dist/assets/{diagram-OA4YK3LP-gC3Q2X6-.js → diagram-OA4YK3LP-ZfN8nvLw.js} +1 -1
  34. package/web/dist/assets/{diagram-WEI45ONY-CABqyjDl.js → diagram-WEI45ONY-S94A6Avm.js} +1 -1
  35. package/web/dist/assets/{ebnfDiagram-CCIWWBDH-cSguXaDC.js → ebnfDiagram-CCIWWBDH-Mpt3QUnw.js} +1 -1
  36. package/web/dist/assets/{erDiagram-Q63AITRT-CoWX37V3.js → erDiagram-Q63AITRT-Bh-eWbdV.js} +1 -1
  37. package/web/dist/assets/{flowDiagram-23GEKE2U-I9DHYjhG.js → flowDiagram-23GEKE2U-BwRtDI6V.js} +1 -1
  38. package/web/dist/assets/{ganttDiagram-NO4QXBWP-DaFnfYTt.js → ganttDiagram-NO4QXBWP-CrZoBmcB.js} +1 -1
  39. package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-CwiXVM_O.js → gitGraphDiagram-IHSO6WYX-giXv1ynk.js} +1 -1
  40. package/web/dist/assets/index-Br-gGWmc.js +24 -0
  41. package/web/dist/assets/{index-CHlrgp0s.css → index-DRFDn87d.css} +1 -1
  42. package/web/dist/assets/{infoDiagram-FWYZ7A6U-DtzIrIpW.js → infoDiagram-FWYZ7A6U-H_5oc4-7.js} +1 -1
  43. package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-B9nk829n.js → ishikawaDiagram-FXEZZL3T-BydiPDX0.js} +1 -1
  44. package/web/dist/assets/{journeyDiagram-5HDEW3XC-xLaPm-ow.js → journeyDiagram-5HDEW3XC-DS1HXms0.js} +1 -1
  45. package/web/dist/assets/{kanban-definition-HUTT4EX6-BnDqLYLC.js → kanban-definition-HUTT4EX6-BjY5SyYu.js} +1 -1
  46. package/web/dist/assets/{linear-Djc7nVo8.js → linear-BvOotjKL.js} +1 -1
  47. package/web/dist/assets/{mermaid.core-B5elhUtV.js → mermaid.core-0iRu7sp-.js} +4 -4
  48. package/web/dist/assets/{mindmap-definition-LN4V7U3C-N-B7EJGs.js → mindmap-definition-LN4V7U3C-DB93zsvL.js} +1 -1
  49. package/web/dist/assets/{papaparse.min-BqM-LJkq.js → papaparse.min-f5Xncedc.js} +1 -1
  50. package/web/dist/assets/{pegDiagram-2B236MQR-BIollSMB.js → pegDiagram-2B236MQR-Dh_U1cFA.js} +1 -1
  51. package/web/dist/assets/{pieDiagram-ENE6RG2P-DkZY6iCd.js → pieDiagram-ENE6RG2P-Csv1qxMm.js} +1 -1
  52. package/web/dist/assets/pilotswarm-DQNSWfNK.js +105 -0
  53. package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-iUDmQIyL.js → quadrantDiagram-ABIIQ3AL-Bo0pcrBM.js} +1 -1
  54. package/web/dist/assets/{railroadDiagram-RFXS5EU6-BSCGJgV7.js → railroadDiagram-RFXS5EU6-CHPvHNWG.js} +1 -1
  55. package/web/dist/assets/{requirementDiagram-TGXJPOKE-9yqcGRUA.js → requirementDiagram-TGXJPOKE-DgX8l-6H.js} +1 -1
  56. package/web/dist/assets/{sankeyDiagram-HTMAVEWB-SPh1plT9.js → sankeyDiagram-HTMAVEWB-xR6MTs5a.js} +1 -1
  57. package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-C0nv5x71.js → sequenceDiagram-DBY2YBRQ-CD_45diB.js} +1 -1
  58. package/web/dist/assets/{sizeCapture-X5ZJPWSS-DMXfUt1o.js → sizeCapture-X5ZJPWSS-BJVEXU0K.js} +1 -1
  59. package/web/dist/assets/{stateDiagram-2N3HPSRC-vjhtquua.js → stateDiagram-2N3HPSRC-Bwu6kd23.js} +1 -1
  60. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-aypsN5jW.js +1 -0
  61. package/web/dist/assets/{swimlanes-5IMT3BWC-Bqewmkt5.js → swimlanes-5IMT3BWC-BmhEzevT.js} +2 -2
  62. package/web/dist/assets/swimlanesDiagram-G3AALYLV-Duf5C4YK.js +8 -0
  63. package/web/dist/assets/{timeline-definition-FHXFAJF6-DYHsdXUL.js → timeline-definition-FHXFAJF6-DFPI_uTI.js} +1 -1
  64. package/web/dist/assets/{vennDiagram-L72KCM5P-Cb7cOHSa.js → vennDiagram-L72KCM5P-DDTpTkBC.js} +1 -1
  65. package/web/dist/assets/{wardleyDiagram-EHGQE667-BHpO1byL.js → wardleyDiagram-EHGQE667-bV2_YVIL.js} +1 -1
  66. package/web/dist/assets/{xychartDiagram-FW5EYKEG-DHDvGPtH.js → xychartDiagram-FW5EYKEG-C7bDIXAV.js} +1 -1
  67. package/web/dist/index.html +3 -3
  68. package/web/dist/assets/channel-B50-C7oJ.js +0 -1
  69. package/web/dist/assets/classDiagram-OUVF2IWQ-CIJlKS0m.js +0 -1
  70. package/web/dist/assets/classDiagram-v2-EOCWNBFH-CIJlKS0m.js +0 -1
  71. package/web/dist/assets/index-bf7RCrAf.js +0 -24
  72. package/web/dist/assets/pilotswarm-Di5s6c69.js +0 -105
  73. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CYIeYmPN.js +0 -1
  74. package/web/dist/assets/swimlanesDiagram-G3AALYLV-BeKhH9C9.js +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pilotswarm",
3
- "version": "0.5.62",
3
+ "version": "0.5.63",
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.62",
84
+ "pilotswarm-sdk": "0.5.63",
85
85
  "react": "^19.2.4",
86
86
  "react-dom": "^19.2.4",
87
87
  "ws": "^8.18.2"
@@ -5838,10 +5838,11 @@ export class PilotSwarmUiController {
5838
5838
  }
5839
5839
  const existing = this.getState().history.bySessionId.get(sessionId)
5840
5840
  || { chat: [], activity: [], events: [], lastSeq: 0 };
5841
+ const liveHistory = this.preservePausedChatWindow(sessionId, existing);
5841
5842
  this.dispatch({
5842
5843
  type: "history/set",
5843
5844
  sessionId,
5844
- history: applyLiveTurnToHistory(existing, event.data, {
5845
+ history: applyLiveTurnToHistory(liveHistory, event.data, {
5845
5846
  sessionId,
5846
5847
  seq: event.liveSeq,
5847
5848
  createdAt: Date.now(),
@@ -5878,7 +5879,10 @@ export class PilotSwarmUiController {
5878
5879
  this._liveTurnIdleTimers.delete(sessionId);
5879
5880
  }
5880
5881
  const state = this.getState();
5881
- const existing = state.history.bySessionId.get(sessionId) || { chat: [], activity: [], lastSeq: 0 };
5882
+ const existing = this.preservePausedChatWindow(
5883
+ sessionId,
5884
+ state.history.bySessionId.get(sessionId) || { chat: [], activity: [], events: [], lastSeq: 0 },
5885
+ );
5882
5886
  if (event.seq <= (existing.lastSeq || 0)) return false;
5883
5887
  this.dispatch({
5884
5888
  type: "history/set",
@@ -5979,6 +5983,31 @@ export class PilotSwarmUiController {
5979
5983
  return true;
5980
5984
  }
5981
5985
 
5986
+ /**
5987
+ * Do not evict the first rendered item while its active chat is paused.
5988
+ * A fixed-size history window normally drops one old item for each live
5989
+ * append. Keeping the same numeric scrollTop then moves the actual text the
5990
+ * user is reading. Grow the in-memory window just enough for arrivals made
5991
+ * during that pause; normal bottom-follow appends resume bounded trimming.
5992
+ */
5993
+ preservePausedChatWindow(sessionId, history) {
5994
+ const state = this.getState();
5995
+ if (state.sessions.activeSessionId !== sessionId || state.ui.followBottom?.chat !== false) {
5996
+ return history;
5997
+ }
5998
+ const currentLimit = Math.max(
5999
+ DEFAULT_HISTORY_EVENT_LIMIT,
6000
+ Number(history?.loadedEventLimit ?? DEFAULT_HISTORY_EVENT_LIMIT) || DEFAULT_HISTORY_EVENT_LIMIT,
6001
+ );
6002
+ const requiredLimit = Math.max(
6003
+ currentLimit,
6004
+ (Array.isArray(history?.events) ? history.events.length : 0) + 1,
6005
+ (Array.isArray(history?.chat) ? history.chat.length : 0) + 4,
6006
+ (Array.isArray(history?.activity) ? history.activity.length : 0) + 1,
6007
+ );
6008
+ return requiredLimit === currentLimit ? history : { ...history, loadedEventLimit: requiredLimit };
6009
+ }
6010
+
5982
6011
  async syncSessionEvents(sessionId) {
5983
6012
  if (!sessionId || typeof this.transport.getSessionEvents !== "function") return;
5984
6013
  const existing = this.getState().history.bySessionId.get(sessionId);
@@ -9178,6 +9207,7 @@ export class PilotSwarmUiController {
9178
9207
  }
9179
9208
 
9180
9209
  paneUsesStickyBottomFollow(pane, state = this.getState()) {
9210
+ if (pane === "chat") return true;
9181
9211
  if (pane === "activity") return true;
9182
9212
  if (pane === "inspector") return state.ui.inspectorTab === "logs";
9183
9213
  return false;
@@ -9251,6 +9281,15 @@ export class PilotSwarmUiController {
9251
9281
  const current = this.getPaneVisualScrollOffset(pane, state);
9252
9282
  const nextOffset = Math.max(0, Math.min(current - delta, maxOffset));
9253
9283
  this.applyPaneVisualScrollOffset(pane, nextOffset, { followBottom: nextOffset >= maxOffset }, state);
9284
+ if (pane === "chat" && delta > 0) {
9285
+ if (current <= 0) {
9286
+ this.handleChatTopHistoryScrollIntent(nextOffset).catch(() => {});
9287
+ } else if (nextOffset <= 0) {
9288
+ this.armChatTopHistoryLoad();
9289
+ }
9290
+ } else if (pane === "chat" && delta < 0) {
9291
+ this.disarmChatTopHistoryLoad();
9292
+ }
9254
9293
  return;
9255
9294
  }
9256
9295
  const current = Math.max(0, Math.min(Number(state.ui.scroll?.[pane]) || 0, maxOffset));
@@ -9273,6 +9312,10 @@ export class PilotSwarmUiController {
9273
9312
  const nextOffset = Math.max(0, Math.min(Number(offset) || 0, maxOffset));
9274
9313
  if (this.paneUsesStickyBottomFollow(pane, state)) {
9275
9314
  this.applyPaneVisualScrollOffset(pane, nextOffset, { followBottom: nextOffset >= maxOffset }, state);
9315
+ if (pane === "chat") {
9316
+ if (maxOffset > 0 && nextOffset <= 0) this.armChatTopHistoryLoad();
9317
+ else this.disarmChatTopHistoryLoad();
9318
+ }
9276
9319
  return;
9277
9320
  }
9278
9321
  this.dispatch({ type: "ui/scroll", pane, offset: nextOffset });
@@ -9308,6 +9351,7 @@ export class PilotSwarmUiController {
9308
9351
 
9309
9352
  await this.maybeAutoExpandActiveHistory(requestedScrollOffset, {
9310
9353
  pages: AUTO_HISTORY_SCROLL_PAGE_COUNT,
9354
+ preserveDomAnchor: options.preserveDomAnchor === true,
9311
9355
  // Always bypass the offset gate. Every caller of this method fires
9312
9356
  // only when the DOM scroller is already AT the top — a direct
9313
9357
  // measurement. The gate is a second, weaker guess that compares a
@@ -9369,7 +9413,7 @@ export class PilotSwarmUiController {
9369
9413
  const pane = this.getScrollablePaneForFocus();
9370
9414
  if (!pane) return;
9371
9415
  if (this.paneUsesStickyBottomFollow(pane)) {
9372
- this.applyPaneVisualScrollOffset(pane, 0, { followBottom: false });
9416
+ this.scrollPaneTo(pane, 0);
9373
9417
  return;
9374
9418
  }
9375
9419
  const inspectorUsesBottomScroll = pane === "inspector" && this.inspectorUsesBottomScroll();
@@ -9649,6 +9693,7 @@ export class PilotSwarmUiController {
9649
9693
  requestedScrollOffset: targetOffset,
9650
9694
  autoTriggered: true,
9651
9695
  pages: options.pages,
9696
+ preserveDomAnchor: options.preserveDomAnchor === true,
9652
9697
  // Chat-driven pull: page backward over renderable message types
9653
9698
  // only, so each page is transcript instead of raw event noise.
9654
9699
  eventTypes: CHAT_HISTORY_EVENT_TYPES,
@@ -9785,9 +9830,12 @@ export class PilotSwarmUiController {
9785
9830
  pane: "chat",
9786
9831
  offset: 0,
9787
9832
  });
9788
- } else if (preserveChatView && previousScrollOffset > 0 && !options.autoTriggered) {
9789
- // EXPLICIT "load older" (the TUI's `e`): jump up to the start
9790
- // of what was just fetched the user asked to SEE it.
9833
+ } else if (preserveChatView && options.autoTriggered && !options.preserveDomAnchor) {
9834
+ // Terminal panes store paused chat as a top offset. Prepending
9835
+ // must advance that offset by the rendered rows inserted above
9836
+ // it, preserving the same visible message. Browser panes use
9837
+ // measured DOM height because rich cards and wrapping do not
9838
+ // match terminal row metrics.
9791
9839
  const nextState = this.getState();
9792
9840
  const nextRenderedLines = this.getActiveChatRenderMetrics(nextState).totalLines;
9793
9841
  const addedLines = Math.max(0, nextRenderedLines - previousRenderedLines);
@@ -9799,14 +9847,6 @@ export class PilotSwarmUiController {
9799
9847
  });
9800
9848
  }
9801
9849
  }
9802
- // AUTO-triggered (scrolling reached the top): change NOTHING. The
9803
- // offset is distance-from-BOTTOM, which prepended content cannot
9804
- // move — leaving it alone keeps the viewport glued to the exact
9805
- // messages the user was reading, and they scroll on into the new
9806
- // page naturally. Adding the delta here teleported the view to
9807
- // the TOP of a 3.3x-larger window — observed as "loading history
9808
- // takes you way back instead of a few pages up".
9809
-
9810
9850
  const stateLabel = history.hasOlderEvents
9811
9851
  ? options.autoTriggered
9812
9852
  ? `Loaded older history page from CMS (${history.loadedEventCount} events loaded)`
@@ -57,6 +57,15 @@ export const activeMoaDashboard = value => value.dashboards.find(d => d.id === v
57
57
  export function updateMoaDashboard(value, dashboardId, patch) {
58
58
  return normalizeMoa({ ...value, dashboards: value.dashboards.map(d => d.id === dashboardId ? { ...d, ...patch } : d) });
59
59
  }
60
+ export function moveMoaDashboard(value, dashboardId, toIndex) {
61
+ const normalized = normalizeMoa(value);
62
+ const fromIndex = normalized.dashboards.findIndex(d => d.id === dashboardId);
63
+ if (fromIndex < 0 || !Number.isInteger(toIndex)) return normalized;
64
+ const dashboards = [...normalized.dashboards];
65
+ const [dashboard] = dashboards.splice(fromIndex, 1);
66
+ dashboards.splice(Math.max(0, Math.min(dashboards.length, toIndex)), 0, dashboard);
67
+ return { ...normalized, dashboards };
68
+ }
60
69
  export function replaceMoaNode(tree, nodeId, next) {
61
70
  if (!tree) return null;
62
71
  if (tree.id === nodeId) return next;
@@ -646,6 +646,7 @@ function updateUiForSessionSelection(state, nextActiveSessionId) {
646
646
  },
647
647
  followBottom: {
648
648
  ...(state.ui.followBottom || {}),
649
+ chat: true,
649
650
  inspector: true,
650
651
  activity: true,
651
652
  },
@@ -1481,7 +1482,7 @@ function baseReducer(state, action) {
1481
1482
  };
1482
1483
 
1483
1484
  case "ui/followBottom": {
1484
- if (action.pane !== "inspector" && action.pane !== "activity") {
1485
+ if (action.pane !== "chat" && action.pane !== "inspector" && action.pane !== "activity") {
1485
1486
  return state;
1486
1487
  }
1487
1488
  const nextFollowBottom = Boolean(action.followBottom);
@@ -1876,14 +1877,15 @@ function baseReducer(state, action) {
1876
1877
 
1877
1878
  case "sessions/selected": {
1878
1879
  state = { ...state, sessions: { ...state.sessions, listDeselected: false } };
1879
- // Per-session chat scroll memory: stash the outgoing session's
1880
- // offset and restore the incoming one's. If new chat arrives on
1881
- // re-entry, history/set's activeChatUpdated reset still snaps to
1882
- // latest — "latest chat, or where you left it".
1880
+ // Per-session chat scroll memory: stash both the outgoing offset
1881
+ // and whether it follows the bottom. A paused reading position
1882
+ // stays paused as new messages arrive and across session switches.
1883
1883
  const previousActiveId = state.sessions.activeSessionId;
1884
1884
  const savedChatScroll = { ...(state.ui.chatScrollBySession || {}) };
1885
+ const savedChatFollowBottom = { ...(state.ui.chatFollowBottomBySession || {}) };
1885
1886
  if (previousActiveId && previousActiveId !== action.sessionId) {
1886
1887
  savedChatScroll[previousActiveId] = Number(state.ui.scroll?.chat) || 0;
1888
+ savedChatFollowBottom[previousActiveId] = state.ui.followBottom?.chat !== false;
1887
1889
  }
1888
1890
  // Per-session prompt drafts: a half-written message belongs to the
1889
1891
  // session it was written in. Stash the outgoing draft (text +
@@ -1936,6 +1938,7 @@ function baseReducer(state, action) {
1936
1938
  ui: {
1937
1939
  ...state.ui,
1938
1940
  chatScrollBySession: savedChatScroll,
1941
+ chatFollowBottomBySession: savedChatFollowBottom,
1939
1942
  promptDraftBySession: savedDrafts,
1940
1943
  prompt: nextPrompt,
1941
1944
  promptCursor: nextPromptCursor,
@@ -1954,6 +1957,7 @@ function baseReducer(state, action) {
1954
1957
  },
1955
1958
  followBottom: {
1956
1959
  ...(state.ui.followBottom || {}),
1960
+ chat: savedChatFollowBottom[action.sessionId] !== false,
1957
1961
  inspector: true,
1958
1962
  activity: true,
1959
1963
  },
@@ -2201,7 +2205,7 @@ function baseReducer(state, action) {
2201
2205
  ...state.history,
2202
2206
  bySessionId: nextHistory,
2203
2207
  },
2204
- ui: activeChatUpdated
2208
+ ui: activeChatUpdated && state.ui.followBottom?.chat !== false
2205
2209
  ? {
2206
2210
  ...state.ui,
2207
2211
  scroll: {
@@ -2222,6 +2226,8 @@ function baseReducer(state, action) {
2222
2226
  for (const id of ids) delete nextOutbox[id];
2223
2227
  const nextChatScroll = { ...(state.ui.chatScrollBySession || {}) };
2224
2228
  for (const id of ids) delete nextChatScroll[id];
2229
+ const nextChatFollowBottom = { ...(state.ui.chatFollowBottomBySession || {}) };
2230
+ for (const id of ids) delete nextChatFollowBottom[id];
2225
2231
  const nextDrafts = { ...(state.ui.promptDraftBySession || {}) };
2226
2232
  for (const id of ids) delete nextDrafts[id];
2227
2233
  return {
@@ -2237,6 +2243,7 @@ function baseReducer(state, action) {
2237
2243
  ui: {
2238
2244
  ...state.ui,
2239
2245
  chatScrollBySession: nextChatScroll,
2246
+ chatFollowBottomBySession: nextChatFollowBottom,
2240
2247
  promptDraftBySession: nextDrafts,
2241
2248
  },
2242
2249
  };
@@ -448,10 +448,14 @@ export function createInitialState({ mode = "local", branding = null, docs = nul
448
448
  activity: 0,
449
449
  filePreview: 0,
450
450
  },
451
- // Per-session chat scroll memory (rows from bottom), restored on
452
- // sessions/selected and evicted with the session's history.
451
+ // Per-session chat scroll memory. While following, the offset is
452
+ // distance from the bottom; once the reader scrolls up it becomes
453
+ // a stable viewport offset from the top. The paired follow flag
454
+ // disambiguates the two modes across session switches.
453
455
  chatScrollBySession: {},
456
+ chatFollowBottomBySession: {},
454
457
  followBottom: {
458
+ chat: true,
455
459
  inspector: true,
456
460
  activity: true,
457
461
  },
@@ -354,6 +354,7 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
354
354
  connectionError: state.connection.error,
355
355
  connectionMode: state.connection.mode,
356
356
  chatScroll: state.ui.scroll.chat,
357
+ followBottom: state.ui.followBottom?.chat !== false,
357
358
  focused: state.ui.focusRegion === "chat",
358
359
  };
359
360
  }, shallowEqualObject);
@@ -449,7 +450,7 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
449
450
  lines: elements,
450
451
  bottomStickyLines,
451
452
  scrollOffset: chatView.chatScroll,
452
- scrollMode: "bottom",
453
+ scrollMode: chatView.followBottom ? "bottom" : "top",
453
454
  paneId: "chat",
454
455
  paneLabel: "Chat",
455
456
  frame,
@@ -1075,6 +1075,18 @@ export function computeAnchoredScrollTop(
1075
1075
  : Math.min(maxScroll, offsetPixels);
1076
1076
  }
1077
1077
 
1078
+ function scrollLineIdentity(line) {
1079
+ if (!line) return "";
1080
+ if (line.kind === "runs") return line.runs?.map((run) => run?.text || "").join("") || "";
1081
+ if (line.kind === "assistantPreview") return `assistant:${line.previewKey || line.messageId || line.id || ""}`;
1082
+ return `${line.kind || "text"}:${line.text || line.value || line.id || ""}`;
1083
+ }
1084
+
1085
+ function scrollBoundaryIdentity(lines, fromEnd = false) {
1086
+ const boundary = fromEnd ? lines.slice(-8) : lines.slice(0, 8);
1087
+ return boundary.map(scrollLineIdentity).join("\u241e");
1088
+ }
1089
+
1078
1090
  function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller, { stickyBottom = false } = {}) {
1079
1091
  const normalizedLines = React.useMemo(() => normalizeLines(lines), [lines]);
1080
1092
  // Programmatic scrollTop assignments fire a 'scroll' event that would
@@ -1087,6 +1099,7 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1087
1099
  scrollMode,
1088
1100
  scrollOffset,
1089
1101
  });
1102
+ const previousContentRef = React.useRef(null);
1090
1103
  // Touch scrolling relies on native momentum for speed: a hard flick keeps
1091
1104
  // scrolling long after the finger lifts. Re-asserting scrollTop from state
1092
1105
  // on every render (live events, status updates) kills that momentum at the
@@ -1116,30 +1129,44 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1116
1129
  const node = ref.current;
1117
1130
  if (!node) return;
1118
1131
  const previousViewportState = previousViewportStateRef.current;
1132
+ const firstLine = scrollBoundaryIdentity(normalizedLines);
1133
+ const lastLine = scrollBoundaryIdentity(normalizedLines, true);
1134
+ const previousContent = previousContentRef.current;
1119
1135
  const interaction = userScrollRef.current;
1120
1136
  const now = typeof performance !== "undefined" ? performance.now() : Date.now();
1121
1137
  const interacting = interaction.touching
1122
1138
  || (now - (interaction.lastUserScrollAt || 0)) < TOUCH_MOMENTUM_GRACE_MS;
1123
1139
  const isEchoOffset = interaction.lastDispatchedOffset != null
1124
1140
  && Math.abs((Number(scrollOffset) || 0) - interaction.lastDispatchedOffset) < 2;
1141
+ const preservePausedStickyScroll = stickyBottom
1142
+ && scrollMode === "top"
1143
+ && previousViewportState?.scrollMode === "top"
1144
+ && previousViewportState?.scrollOffset === scrollOffset;
1145
+ // A backward page inserts DOM above the reader. Top-anchored paused
1146
+ // state normally preserves scrollTop, which would push the same visible
1147
+ // message down by the inserted height. Compensate with the measured DOM
1148
+ // height delta. Appended live text keeps the old scrollTop unchanged.
1149
+ const prependedWhilePaused = stickyBottom
1150
+ && previousContent?.lastLine === lastLine
1151
+ && previousContent?.firstLine !== firstLine
1152
+ && node.scrollHeight > previousContent.scrollHeight
1153
+ && (preservePausedStickyScroll || node.scrollTop <= PROGRAMMATIC_SCROLL_TOLERANCE_PX);
1125
1154
  if (
1126
- interacting
1155
+ !prependedWhilePaused
1156
+ && interacting
1127
1157
  && scrollMode === previousViewportState?.scrollMode
1128
1158
  && (scrollOffset === previousViewportState?.scrollOffset || isEchoOffset)
1129
1159
  ) {
1130
1160
  previousViewportStateRef.current = { scrollMode, scrollOffset };
1161
+ previousContentRef.current = { firstLine, lastLine, scrollHeight: node.scrollHeight };
1131
1162
  return;
1132
1163
  }
1133
- const preservePausedStickyScroll = stickyBottom
1134
- && scrollMode === "top"
1135
- && previousViewportState?.scrollMode === "top"
1136
- && previousViewportState?.scrollOffset === scrollOffset;
1137
- const nextScrollTop = computeAnchoredScrollTop(
1138
- node,
1139
- scrollOffset,
1140
- scrollMode,
1141
- preservePausedStickyScroll,
1142
- );
1164
+ const nextScrollTop = prependedWhilePaused
1165
+ ? Math.max(0, Math.min(
1166
+ node.scrollTop + (node.scrollHeight - previousContent.scrollHeight),
1167
+ node.scrollHeight - node.clientHeight,
1168
+ ))
1169
+ : computeAnchoredScrollTop(node, scrollOffset, scrollMode, preservePausedStickyScroll);
1143
1170
  if (Math.abs(node.scrollTop - nextScrollTop) > PROGRAMMATIC_SCROLL_TOLERANCE_PX) {
1144
1171
  const pendingProgrammaticScroll = { target: nextScrollTop };
1145
1172
  programmaticScrollRef.current = pendingProgrammaticScroll;
@@ -1152,10 +1179,20 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1152
1179
  }
1153
1180
  });
1154
1181
  }
1182
+ if (prependedWhilePaused && paneKey && typeof controller.updatePaneScrollFromViewport === "function") {
1183
+ // The measured DOM compensation above is the new durable paused
1184
+ // position. Persist it too: otherwise switching sessions and back
1185
+ // reapplies the pre-page offset and moves the reader by the full
1186
+ // height of the newly inserted history.
1187
+ const rowOffset = Math.max(0, nextScrollTop) / SCROLL_ROW_HEIGHT;
1188
+ userScrollRef.current.lastDispatchedOffset = rowOffset;
1189
+ controller.updatePaneScrollFromViewport(paneKey, rowOffset, { atBottom: false });
1190
+ }
1155
1191
  previousViewportStateRef.current = {
1156
1192
  scrollMode,
1157
1193
  scrollOffset,
1158
1194
  };
1195
+ previousContentRef.current = { firstLine, lastLine, scrollHeight: node.scrollHeight };
1159
1196
  }, [normalizedLines, ref, scrollMode, scrollOffset, stickyBottom, viewportRevision]);
1160
1197
 
1161
1198
  const dispatchScrollOffset = useFrameCoalescedCallback((offset) => {
@@ -1189,6 +1226,13 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1189
1226
  rowOffset,
1190
1227
  { atBottom: isScrollViewportAtBottom(node) },
1191
1228
  );
1229
+ // Pausing bottom-follow changes chat to top-anchored scroll state,
1230
+ // but reaching the DOM's actual top must still arm backward CMS
1231
+ // paging. Keep this next to the native measurement so the history
1232
+ // gate does not depend on which anchoring mode currently renders.
1233
+ if (paneKey === "chat" && node.scrollTop <= PROGRAMMATIC_SCROLL_TOLERANCE_PX) {
1234
+ controller.armChatTopHistoryLoad?.();
1235
+ }
1192
1236
  return;
1193
1237
  }
1194
1238
 
@@ -1207,11 +1251,11 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1207
1251
 
1208
1252
  const onWheel = React.useCallback((event) => {
1209
1253
  const node = ref.current;
1210
- if (!node || paneKey !== "chat" || scrollMode !== "bottom" || event.deltaY >= 0) return;
1254
+ if (!node || paneKey !== "chat" || event.deltaY >= 0) return;
1211
1255
  if (node.scrollTop > PROGRAMMATIC_SCROLL_TOLERANCE_PX) return;
1212
1256
  const maxScroll = Math.max(0, node.scrollHeight - node.clientHeight);
1213
- controller.handleChatTopHistoryScrollIntent?.(maxScroll / SCROLL_ROW_HEIGHT);
1214
- }, [controller, paneKey, ref, scrollMode]);
1257
+ controller.handleChatTopHistoryScrollIntent?.(maxScroll / SCROLL_ROW_HEIGHT, { preserveDomAnchor: true });
1258
+ }, [controller, paneKey, ref]);
1215
1259
 
1216
1260
  // Touch equivalent of the wheel-at-top gesture: touch devices never fire
1217
1261
  // wheel events, and once scrollTop sits at 0 a further swipe-down emits no
@@ -1221,9 +1265,9 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1221
1265
  const touchPullRef = React.useRef({ startY: null, fired: false });
1222
1266
  const onTouchStart = React.useCallback((event) => {
1223
1267
  userScrollRef.current.touching = true;
1224
- if (paneKey !== "chat" || scrollMode !== "bottom") return;
1268
+ if (paneKey !== "chat") return;
1225
1269
  touchPullRef.current = { startY: event.touches?.[0]?.clientY ?? null, fired: false };
1226
- }, [paneKey, scrollMode]);
1270
+ }, [paneKey]);
1227
1271
  const onTouchEnd = React.useCallback(() => {
1228
1272
  userScrollRef.current.touching = false;
1229
1273
  // Momentum continues past the finger lift; onScroll keeps refreshing
@@ -1233,7 +1277,7 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1233
1277
  const onTouchMove = React.useCallback((event) => {
1234
1278
  const node = ref.current;
1235
1279
  const pull = touchPullRef.current;
1236
- if (!node || paneKey !== "chat" || scrollMode !== "bottom") return;
1280
+ if (!node || paneKey !== "chat") return;
1237
1281
  if (pull.fired || pull.startY == null) return;
1238
1282
  if (node.scrollTop > PROGRAMMATIC_SCROLL_TOLERANCE_PX) return;
1239
1283
  const y = event.touches?.[0]?.clientY;
@@ -1245,8 +1289,8 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1245
1289
  // on narrow viewports). The wheel path keeps the handshake — one
1246
1290
  // physical scroll emits MANY wheel events, so it needs debouncing.
1247
1291
  const maxScroll = Math.max(0, node.scrollHeight - node.clientHeight);
1248
- controller.handleChatTopHistoryScrollIntent?.(maxScroll / SCROLL_ROW_HEIGHT + 1, { force: true });
1249
- }, [controller, paneKey, ref, scrollMode]);
1292
+ controller.handleChatTopHistoryScrollIntent?.(maxScroll / SCROLL_ROW_HEIGHT + 1, { force: true, preserveDomAnchor: true });
1293
+ }, [controller, paneKey, ref]);
1250
1294
 
1251
1295
  return { normalizedLines, onScroll, onWheel, onTouchStart, onTouchMove, onTouchEnd };
1252
1296
  }
@@ -5570,10 +5614,14 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
5570
5614
  || focused.tagName === "SELECT"
5571
5615
  || focused.isContentEditable
5572
5616
  ));
5573
- if (focused !== activeButton && !isTypingTarget) {
5617
+ if (viewState.focused && focused !== activeButton && !isTypingTarget) {
5574
5618
  activeButton.focus({ preventScroll: true });
5575
5619
  }
5576
- activeButton.scrollIntoView({ block: "nearest" });
5620
+ // Selection can change from outside this pane (deep links, newly
5621
+ // created sessions, MoA focus and other navigation surfaces). Center
5622
+ // the row once for that selection so its surrounding sessions remain
5623
+ // visible, without re-centering on routine status/catalog refreshes.
5624
+ activeButton.scrollIntoView({ block: "center" });
5577
5625
  }, [activeRowRevealKey, viewState.activeSessionId, viewState.focused, viewState.modalOpen, viewState.sessionsFlat]);
5578
5626
 
5579
5627
  const panelActions = React.createElement(React.Fragment, null,
@@ -6492,6 +6540,7 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
6492
6540
  activeSessionStatus: activeSessionId ? String(state.sessions.byId[activeSessionId]?.status || "").toLowerCase() : "",
6493
6541
  focused: state.ui.focusRegion === "chat",
6494
6542
  scroll: state.ui.scroll.chat,
6543
+ followBottom: state.ui.followBottom?.chat !== false,
6495
6544
  // Viewer identity — so the transcript can say "You" for the viewer's
6496
6545
  // own messages and name others (with an "(owner)" tag).
6497
6546
  authPrincipal: state.auth?.principal || null,
@@ -6641,7 +6690,8 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
6641
6690
  bottomStickyLines: activityInHeader ? EMPTY_ARRAY : stickyBottom,
6642
6691
  reserveBottomSticky: !activityInHeader,
6643
6692
  scrollOffset: viewState.scroll,
6644
- scrollMode: "bottom",
6693
+ scrollMode: viewState.followBottom ? "bottom" : "top",
6694
+ stickyBottom: true,
6645
6695
  paneKey: "chat",
6646
6696
  ariaLive: "polite",
6647
6697
  className: "is-wrapped",
@@ -9086,7 +9136,7 @@ function PromptComposer({ controller, mobile, compact = false, active = true, on
9086
9136
  onClick: cancelPending,
9087
9137
  }, selectedQueued ? "Delete" : "Cancel")
9088
9138
  : null,
9089
- !compact && (promptState.canStopTurn || stoppingTurn)
9139
+ (promptState.canStopTurn || stoppingTurn)
9090
9140
  ? React.createElement("button", {
9091
9141
  type: "button",
9092
9142
  className: `ps-stop-button${stoppingTurn ? " is-stopping" : ""}`,
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-l907LpMA.js";import{p as f}from"./chunk-JWPE2WC7-Cj5lvEfN.js";import{_ as n,l as o}from"./mermaid.core-B5elhUtV.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CminxBoG.js";import"./pilotswarm-Di5s6c69.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-Dn5qSVAS.js";import{p as f}from"./chunk-JWPE2WC7-BHm6Udd8.js";import{_ as n,l as o}from"./mermaid.core-0iRu7sp-.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CVokaaE_.js";import"./pilotswarm-DQNSWfNK.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-B5elhUtV.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-0iRu7sp-.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};