pilotswarm 0.5.75 → 0.5.77

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 (72) hide show
  1. package/package.json +2 -2
  2. package/ui/core/src/controller.js +4 -1
  3. package/ui/core/src/history.js +26 -10
  4. package/ui/react/src/web-app.js +103 -39
  5. package/web/dist/assets/{abnfDiagram-VRR7QNED-CRN_CPXb.js → abnfDiagram-VRR7QNED-CKO_DwJV.js} +1 -1
  6. package/web/dist/assets/{arc-CXqBVBpG.js → arc-DNYWrpvl.js} +1 -1
  7. package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-DOA41VEX.js → architectureDiagram-ZJ3FMSHR-BuYVWnLH.js} +1 -1
  8. package/web/dist/assets/{blockDiagram-677ZJIJ3-BCa9kQVE.js → blockDiagram-677ZJIJ3-Cl_IMZqi.js} +1 -1
  9. package/web/dist/assets/{c4Diagram-LMCZKHZV-CccO5uAf.js → c4Diagram-LMCZKHZV-Dp8EWK6S.js} +1 -1
  10. package/web/dist/assets/channel-DTKy7mHi.js +1 -0
  11. package/web/dist/assets/{chunk-2Q5K7J3B-BOzOMW0c.js → chunk-2Q5K7J3B-Dja0nryh.js} +1 -1
  12. package/web/dist/assets/{chunk-32BRIVSS-DyeNS6EA.js → chunk-32BRIVSS-BfmHxlTO.js} +1 -1
  13. package/web/dist/assets/{chunk-5VM5RSS4-Cl2puolj.js → chunk-5VM5RSS4-DJ3zmwrq.js} +1 -1
  14. package/web/dist/assets/{chunk-EX3LRPZG-CU792nsG.js → chunk-EX3LRPZG-B6SN8xEo.js} +1 -1
  15. package/web/dist/assets/{chunk-JWPE2WC7-DI-c4Jvn.js → chunk-JWPE2WC7-Dlgqhr3G.js} +1 -1
  16. package/web/dist/assets/{chunk-MOJQB5TN-CVuPn9RU.js → chunk-MOJQB5TN-Dn-J2ayj.js} +1 -1
  17. package/web/dist/assets/{chunk-RYQCIY6F-CD34aFNh.js → chunk-RYQCIY6F-Af9a3V0M.js} +1 -1
  18. package/web/dist/assets/{chunk-V7JOEXUC-BwQU46DX.js → chunk-V7JOEXUC-M3md4NxL.js} +1 -1
  19. package/web/dist/assets/{chunk-VR4S4FIN-CL1JK1sJ.js → chunk-VR4S4FIN-ee4UcZX-.js} +1 -1
  20. package/web/dist/assets/{chunk-XXDRQBXY-UOxFbxIp.js → chunk-XXDRQBXY-C1o9OH3_.js} +1 -1
  21. package/web/dist/assets/classDiagram-OUVF2IWQ-MfqAYpCn.js +1 -0
  22. package/web/dist/assets/classDiagram-v2-EOCWNBFH-MfqAYpCn.js +1 -0
  23. package/web/dist/assets/{cose-bilkent-JH36ORCC-CfQP19CB.js → cose-bilkent-JH36ORCC-CRxjVQDN.js} +1 -1
  24. package/web/dist/assets/{cynefin-VYW2F7L2-C9ifcKtK.js → cynefin-VYW2F7L2-MSAXYGkZ.js} +1 -1
  25. package/web/dist/assets/{cynefinDiagram-TSTJHNR4-IwcrpJT3.js → cynefinDiagram-TSTJHNR4-BPutBNN1.js} +1 -1
  26. package/web/dist/assets/{dagre-VKFMJZFB-BzTGLYBP.js → dagre-VKFMJZFB-_W1WeW3E.js} +1 -1
  27. package/web/dist/assets/{diagram-FQU43EPY-D9X1NgQz.js → diagram-FQU43EPY-DT_0zQl9.js} +1 -1
  28. package/web/dist/assets/{diagram-G47NLZAW-BkdiUvXU.js → diagram-G47NLZAW-HBy_Ck1W.js} +1 -1
  29. package/web/dist/assets/{diagram-NH7WQ7WH-nCeiDJLN.js → diagram-NH7WQ7WH-DyQQlTUp.js} +1 -1
  30. package/web/dist/assets/{diagram-OA4YK3LP-CXdSE6QP.js → diagram-OA4YK3LP-LJ7WYOVk.js} +1 -1
  31. package/web/dist/assets/{diagram-WEI45ONY-BNDoTBRl.js → diagram-WEI45ONY-B83fa0oq.js} +1 -1
  32. package/web/dist/assets/{ebnfDiagram-CCIWWBDH-BSsQ9Do8.js → ebnfDiagram-CCIWWBDH-ZbAvBv6c.js} +1 -1
  33. package/web/dist/assets/{erDiagram-Q63AITRT-BTkFSkJi.js → erDiagram-Q63AITRT-BNe1kdom.js} +1 -1
  34. package/web/dist/assets/{flowDiagram-23GEKE2U-BGEZ9wur.js → flowDiagram-23GEKE2U-DDlbRR4H.js} +1 -1
  35. package/web/dist/assets/{ganttDiagram-NO4QXBWP-WUAUhEpP.js → ganttDiagram-NO4QXBWP-DRvGvhmv.js} +1 -1
  36. package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-BQZQl2ZR.js → gitGraphDiagram-IHSO6WYX-Bj5CkOw6.js} +1 -1
  37. package/web/dist/assets/index-CVQyeZNA.js +24 -0
  38. package/web/dist/assets/index-mtWlthKI.css +1 -0
  39. package/web/dist/assets/{infoDiagram-FWYZ7A6U-BP9xJx0Z.js → infoDiagram-FWYZ7A6U-BB-pO5BG.js} +1 -1
  40. package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-Kz3l0pEF.js → ishikawaDiagram-FXEZZL3T-fplKz6J9.js} +1 -1
  41. package/web/dist/assets/{journeyDiagram-5HDEW3XC-KA8zfX2j.js → journeyDiagram-5HDEW3XC-CZXJ9si6.js} +1 -1
  42. package/web/dist/assets/{kanban-definition-HUTT4EX6-Cvy5U2tj.js → kanban-definition-HUTT4EX6-CL9u5Pmv.js} +1 -1
  43. package/web/dist/assets/{linear-DQ0Ynwre.js → linear-WwQWwN2U.js} +1 -1
  44. package/web/dist/assets/{mermaid.core-C_2W6ze2.js → mermaid.core-1gBc6FeB.js} +4 -4
  45. package/web/dist/assets/{mindmap-definition-LN4V7U3C-CPkdv3pS.js → mindmap-definition-LN4V7U3C-CtKgb3BP.js} +1 -1
  46. package/web/dist/assets/{papaparse.min-gRC4B4xh.js → papaparse.min-Dk5F4KnO.js} +1 -1
  47. package/web/dist/assets/{pegDiagram-2B236MQR-BEmzoVBF.js → pegDiagram-2B236MQR-rkKhqRQO.js} +1 -1
  48. package/web/dist/assets/{pieDiagram-ENE6RG2P-Cs-r2GVr.js → pieDiagram-ENE6RG2P-VfaXje3o.js} +1 -1
  49. package/web/dist/assets/pilotswarm-BvFGjArK.js +109 -0
  50. package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-D1SNNoOk.js → quadrantDiagram-ABIIQ3AL-Cw6aMTCy.js} +1 -1
  51. package/web/dist/assets/{railroadDiagram-RFXS5EU6-BH9sxNL4.js → railroadDiagram-RFXS5EU6-Dju0kwmP.js} +1 -1
  52. package/web/dist/assets/{requirementDiagram-TGXJPOKE-ucbqSBch.js → requirementDiagram-TGXJPOKE-BbyvxgRb.js} +1 -1
  53. package/web/dist/assets/{sankeyDiagram-HTMAVEWB-9LzvC1Q9.js → sankeyDiagram-HTMAVEWB-Do_VdVhS.js} +1 -1
  54. package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-yVbJ1mbs.js → sequenceDiagram-DBY2YBRQ-CKqDjXwf.js} +1 -1
  55. package/web/dist/assets/{sizeCapture-X5ZJPWSS-aH-owuq8.js → sizeCapture-X5ZJPWSS-sVG1J5D7.js} +1 -1
  56. package/web/dist/assets/{stateDiagram-2N3HPSRC-eNxPWwbu.js → stateDiagram-2N3HPSRC-CfjQilyb.js} +1 -1
  57. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CYw2jLW6.js +1 -0
  58. package/web/dist/assets/{swimlanes-5IMT3BWC-CUOaCY_Z.js → swimlanes-5IMT3BWC-Csum4YeF.js} +2 -2
  59. package/web/dist/assets/swimlanesDiagram-G3AALYLV-CbG-9P20.js +8 -0
  60. package/web/dist/assets/{timeline-definition-FHXFAJF6-CP6SzyRW.js → timeline-definition-FHXFAJF6-Cw6UCQjz.js} +1 -1
  61. package/web/dist/assets/{vennDiagram-L72KCM5P-ByPylANx.js → vennDiagram-L72KCM5P-b_571af9.js} +1 -1
  62. package/web/dist/assets/{wardleyDiagram-EHGQE667-QfPO6uUo.js → wardleyDiagram-EHGQE667-DL_0-lVh.js} +1 -1
  63. package/web/dist/assets/{xychartDiagram-FW5EYKEG-CNvueYN7.js → xychartDiagram-FW5EYKEG-BqKYzu8y.js} +1 -1
  64. package/web/dist/index.html +3 -3
  65. package/web/dist/assets/channel-Df1De1np.js +0 -1
  66. package/web/dist/assets/classDiagram-OUVF2IWQ-ee52oYsq.js +0 -1
  67. package/web/dist/assets/classDiagram-v2-EOCWNBFH-ee52oYsq.js +0 -1
  68. package/web/dist/assets/index-CSMHeAhZ.css +0 -1
  69. package/web/dist/assets/index-DQFBdVYX.js +0 -24
  70. package/web/dist/assets/pilotswarm-qQVUkWws.js +0 -109
  71. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-B5tcxao1.js +0 -1
  72. package/web/dist/assets/swimlanesDiagram-G3AALYLV-Dz8gcrTg.js +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pilotswarm",
3
- "version": "0.5.75",
3
+ "version": "0.5.77",
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.75",
84
+ "pilotswarm-sdk": "0.5.77",
85
85
  "react": "^19.2.4",
86
86
  "react-dom": "^19.2.4",
87
87
  "ws": "^8.18.2"
@@ -9698,6 +9698,7 @@ export class PilotSwarmUiController {
9698
9698
  await this.maybeAutoExpandActiveHistory(requestedScrollOffset, {
9699
9699
  pages: AUTO_HISTORY_SCROLL_PAGE_COUNT,
9700
9700
  preserveDomAnchor: options.preserveDomAnchor === true,
9701
+ onLoadStarted: options.onLoadStarted,
9701
9702
  // Always bypass the offset gate. Every caller of this method fires
9702
9703
  // only when the DOM scroller is already AT the top — a direct
9703
9704
  // measurement. The gate is a second, weaker guess that compares a
@@ -10035,7 +10036,7 @@ export class PilotSwarmUiController {
10035
10036
  if (targetOffset < maxOffset) return;
10036
10037
  }
10037
10038
 
10038
- await this.expandSessionHistory(sessionId, {
10039
+ const load = this.expandSessionHistory(sessionId, {
10039
10040
  requestedScrollOffset: targetOffset,
10040
10041
  autoTriggered: true,
10041
10042
  pages: options.pages,
@@ -10044,6 +10045,8 @@ export class PilotSwarmUiController {
10044
10045
  // only, so each page is transcript instead of raw event noise.
10045
10046
  eventTypes: CHAT_HISTORY_EVENT_TYPES,
10046
10047
  });
10048
+ options.onLoadStarted?.();
10049
+ await load;
10047
10050
  }
10048
10051
 
10049
10052
  async expandSessionHistory(sessionId, options = {}) {
@@ -630,6 +630,16 @@ function buildChatMessage(event, role) {
630
630
  ? event.data.messageId
631
631
  : null;
632
632
  const chatRole = deriveChatRole(event, role, text);
633
+ // Model commentary is a durable user-facing progress update. Once it has
634
+ // been recorded as assistant.message it belongs in the transcript like
635
+ // any other agent message; only transient live-turn output and an
636
+ // unclassified/final response use the preview shell. Treating commentary
637
+ // as a preview hid milestone updates behind a collapsed "Agent update"
638
+ // disclosure even though the model had explicitly sent them to the user.
639
+ const assistantPhase = chatRole === "assistant"
640
+ ? String(event?.data?.phase || "").trim().toLowerCase()
641
+ : "";
642
+ const isAssistantCommentary = assistantPhase === "commentary";
633
643
  return {
634
644
  id: `${event.sessionId}:${event.seq}`,
635
645
  role: chatRole,
@@ -640,14 +650,16 @@ function buildChatMessage(event, role) {
640
650
  ...(sender ? { sender } : {}),
641
651
  ...(attachments.length > 0 ? { attachments } : {}),
642
652
  ...(messageId ? { messageId } : {}),
643
- ...(chatRole === "assistant" ? {
653
+ ...(chatRole === "assistant" ? (isAssistantCommentary ? {
654
+ assistantCommentary: true,
655
+ } : {
644
656
  assistantPreview: true,
645
657
  responsePending: true,
646
658
  responseFinal: false,
647
659
  responseCanFinalize: !event?.data?.parentToolCallId && !event?.data?.toolRequests?.length,
648
660
  ...(typeof event?.data?.reasoningText === "string" && event.data.reasoningText
649
661
  ? { liveReasoningText: event.data.reasoningText } : {}),
650
- } : {}),
662
+ }) : {}),
651
663
  };
652
664
  }
653
665
 
@@ -1487,14 +1499,18 @@ export function appendEventToHistory(history, event) {
1487
1499
  if (liveIndex >= 0) {
1488
1500
  const settledAt = Date.now();
1489
1501
  const liveStartedAt = Number(liveItem?.liveStartedAt || liveItem?.createdAt) || settledAt;
1490
- // Keep the disclosure identity even for short streams. Committing
1491
- // an interim message must not reset expansion or its scroll box.
1492
- Object.assign(message, {
1493
- streamSettling: true,
1494
- liveKey: liveItem?.liveKey || message.messageId,
1495
- liveStartedAt,
1496
- liveReasoningText: message.liveReasoningText || liveItem?.reasoningText || "",
1497
- });
1502
+ if (!message.assistantCommentary) {
1503
+ // Keep the disclosure identity even for short streams.
1504
+ // Committing an answer must not reset expansion or its scroll
1505
+ // box. Durable commentary deliberately leaves this shell and
1506
+ // becomes an ordinary visible transcript message.
1507
+ Object.assign(message, {
1508
+ streamSettling: true,
1509
+ liveKey: liveItem?.liveKey || message.messageId,
1510
+ liveStartedAt,
1511
+ liveReasoningText: message.liveReasoningText || liveItem?.reasoningText || "",
1512
+ });
1513
+ }
1498
1514
  next.chat.splice(Math.min(liveIndex, next.chat.length), 0, message);
1499
1515
  }
1500
1516
  else next.chat.push(message);
@@ -94,7 +94,8 @@ const SCROLL_BOTTOM_EPSILON_PX = 0.5;
94
94
  const PROGRAMMATIC_SCROLL_TOLERANCE_PX = SCROLL_BOTTOM_EPSILON_PX;
95
95
  // Minimum downward finger travel (px) while at the top of the chat pane before
96
96
  // a touch pull counts as a load-older-history request.
97
- const TOUCH_TOP_PULL_THRESHOLD_PX = 24;
97
+ const TOUCH_TOP_PULL_THRESHOLD_PX = 56;
98
+ const TOUCH_TOP_PULL_MAX_PX = 84;
98
99
  // How long after the last user-driven scroll event the pane still counts as
99
100
  // momentum-scrolling (native flick glide), during which programmatic scrollTop
100
101
  // restores are suppressed so they don't kill the glide.
@@ -1107,7 +1108,11 @@ function scrollBoundaryIdentity(lines, fromEnd = false) {
1107
1108
  return boundary.map(scrollLineIdentity).join("\u241e");
1108
1109
  }
1109
1110
 
1110
- function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller, { stickyBottom = false } = {}) {
1111
+ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller, {
1112
+ stickyBottom = false,
1113
+ historyPullEnabled = false,
1114
+ historySessionId = null,
1115
+ } = {}) {
1111
1116
  const normalizedLines = React.useMemo(() => normalizeLines(lines), [lines]);
1112
1117
  // Programmatic scrollTop assignments fire a 'scroll' event that would
1113
1118
  // otherwise call onScroll → dispatch ui/scroll → clobber the user's
@@ -1122,6 +1127,9 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1122
1127
  const previousContentRef = React.useRef(null);
1123
1128
  const historyScrollRef = React.useRef(null);
1124
1129
  React.useEffect(() => () => { historyScrollRef.current = null; }, []);
1130
+ const [topPullDistance, setTopPullDistance] = React.useState(0);
1131
+ const [loadingHistorySessionId, setLoadingHistorySessionId] = React.useState(null);
1132
+ React.useEffect(() => { setTopPullDistance(0); }, [historySessionId]);
1125
1133
  // Touch scrolling relies on native momentum for speed: a hard flick keeps
1126
1134
  // scrolling long after the finger lifts. Re-asserting scrollTop from state
1127
1135
  // on every render (live events, status updates) kills that momentum at the
@@ -1309,7 +1317,7 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1309
1317
  const node = ref.current;
1310
1318
  const state = controller.getState();
1311
1319
  const sessionId = state.sessions.activeSessionId;
1312
- if (!node || !sessionId || historyScrollRef.current) return;
1320
+ if (!node || !sessionId || !historyPullEnabled || historyScrollRef.current) return;
1313
1321
  const top = node.getBoundingClientRect().top;
1314
1322
  const element = [...node.children].find(child => child.textContent?.trim() && child.getBoundingClientRect().bottom > top + 6);
1315
1323
  const pending = {
@@ -1320,15 +1328,17 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1320
1328
  historyScrollRef.current = pending;
1321
1329
  const load = controller.handleChatTopHistoryScrollIntent?.(
1322
1330
  (node.scrollHeight - node.clientHeight) / SCROLL_ROW_HEIGHT,
1323
- { force: true, preserveDomAnchor: true },
1331
+ { force: true, preserveDomAnchor: true,
1332
+ onLoadStarted: () => setLoadingHistorySessionId(sessionId) },
1324
1333
  );
1325
1334
  Promise.resolve(load).catch(() => {}).finally(() => {
1335
+ setLoadingHistorySessionId(current => current === sessionId ? null : current);
1326
1336
  if (historyScrollRef.current !== pending) return;
1327
1337
  const oldest = controller.getState().history.bySessionId.get(sessionId)?.events?.[0]?.seq;
1328
1338
  if (!(oldest < pending.oldestSeq)) historyScrollRef.current = null;
1329
1339
  else setViewportRevision(revision => revision + 1);
1330
1340
  });
1331
- }, [controller, ref]);
1341
+ }, [controller, historyPullEnabled, ref]);
1332
1342
 
1333
1343
  const onWheel = React.useCallback((event) => {
1334
1344
  const node = ref.current;
@@ -1354,13 +1364,18 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1354
1364
  // scroll events either — so mobile had no way to request older history.
1355
1365
  // A downward pull that starts while the pane is at (or near) the top fires
1356
1366
  // the same top-history intent, once per gesture.
1357
- const touchPullRef = React.useRef({ startY: null, fired: false });
1367
+ const touchPullRef = React.useRef({ startY: null, distance: 0 });
1358
1368
  const onTouchStart = React.useCallback((event) => {
1359
1369
  userScrollRef.current.touching = true;
1360
1370
  if (historyScrollRef.current?.applied) historyScrollRef.current = null;
1361
- if (paneKey !== "chat") return;
1362
- touchPullRef.current = { startY: event.touches?.[0]?.clientY ?? null, fired: false };
1363
- }, [paneKey]);
1371
+ const node = ref.current;
1372
+ touchPullRef.current = {
1373
+ startY: historyPullEnabled && loadingHistorySessionId !== historySessionId
1374
+ && paneKey === "chat" && node?.scrollTop <= PROGRAMMATIC_SCROLL_TOLERANCE_PX
1375
+ ? event.touches?.[0]?.clientY ?? null : null,
1376
+ distance: 0,
1377
+ };
1378
+ }, [historyPullEnabled, historySessionId, loadingHistorySessionId, paneKey, ref]);
1364
1379
  const onTouchEnd = React.useCallback(() => {
1365
1380
  userScrollRef.current.touching = false;
1366
1381
  // Momentum continues past the finger lift; onScroll keeps refreshing
@@ -1369,6 +1384,16 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1369
1384
  if (historyScrollRef.current?.applied) {
1370
1385
  historyScrollRef.current.releaseAt = performance.now() + HISTORY_SCROLL_GESTURE_GAP_MS;
1371
1386
  }
1387
+ const distance = touchPullRef.current.distance;
1388
+ touchPullRef.current = { startY: null, distance: 0 };
1389
+ setTopPullDistance(0);
1390
+ if (distance >= TOUCH_TOP_PULL_THRESHOLD_PX
1391
+ && ref.current?.scrollTop <= PROGRAMMATIC_SCROLL_TOLERANCE_PX) requestHistoryPage();
1392
+ }, [ref, requestHistoryPage]);
1393
+ const onTouchCancel = React.useCallback(() => {
1394
+ userScrollRef.current.touching = false;
1395
+ touchPullRef.current = { startY: null, distance: 0 };
1396
+ setTopPullDistance(0);
1372
1397
  }, []);
1373
1398
  const onTouchMove = React.useCallback((event) => {
1374
1399
  const node = ref.current;
@@ -1379,17 +1404,14 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1379
1404
  event.preventDefault();
1380
1405
  return;
1381
1406
  }
1382
- if (pull.fired || pull.startY == null) return;
1407
+ if (pull.startY == null) return;
1383
1408
  if (node.scrollTop > PROGRAMMATIC_SCROLL_TOLERANCE_PX) return;
1384
1409
  const y = event.touches?.[0]?.clientY;
1385
- if (y == null || y - pull.startY < TOUCH_TOP_PULL_THRESHOLD_PX) return;
1386
- pull.fired = true;
1387
- // A deliberate pull at the top of the pane is an unambiguous request:
1388
- // Request one page at the measured DOM boundary. The shared gesture
1389
- // guard absorbs repeated wheel events and the remainder of this pull.
1390
- event.preventDefault();
1391
- requestHistoryPage();
1392
- }, [paneKey, ref, requestHistoryPage]);
1410
+ if (y == null) return;
1411
+ pull.distance = Math.min(TOUCH_TOP_PULL_MAX_PX, Math.max(0, y - pull.startY));
1412
+ setTopPullDistance(pull.distance);
1413
+ if (pull.distance > 0) event.preventDefault();
1414
+ }, [paneKey, ref]);
1393
1415
 
1394
1416
  React.useEffect(() => {
1395
1417
  const node = ref.current;
@@ -1404,7 +1426,8 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
1404
1426
  };
1405
1427
  }, [ref, onWheel, onTouchMove]);
1406
1428
 
1407
- return { normalizedLines, onScroll, onTouchStart, onTouchEnd };
1429
+ return { normalizedLines, onScroll, onTouchStart, onTouchEnd, onTouchCancel,
1430
+ topPullDistance, loadingOlderHistory: loadingHistorySessionId === historySessionId };
1408
1431
  }
1409
1432
 
1410
1433
  function Runs({ runs, theme }) {
@@ -3785,7 +3808,9 @@ const PreviewMessageContent = React.memo(function PreviewMessageContent({ conten
3785
3808
  });
3786
3809
 
3787
3810
  const AssistantPreviewCard = React.memo(function AssistantPreviewCard({ line, theme, controller }) {
3788
- const [open, setOpen] = React.useState(false);
3811
+ // Saved updates should be readable immediately. The viewport below keeps
3812
+ // long updates scrollable without taking over the whole chat pane.
3813
+ const [open, setOpen] = React.useState(line.text === "Agent update");
3789
3814
  const [revealed, setRevealed] = React.useState(
3790
3815
  !line.liveStartedAt || Date.now() - line.liveStartedAt >= 200,
3791
3816
  );
@@ -3794,6 +3819,10 @@ const AssistantPreviewCard = React.memo(function AssistantPreviewCard({ line, th
3794
3819
  const followRef = React.useRef(true);
3795
3820
  const snapshotRef = React.useRef(null);
3796
3821
  const expanded = open || line.final;
3822
+ React.useLayoutEffect(() => {
3823
+ // A live preview can become a saved update in the same card.
3824
+ if (line.text === "Agent update") setOpen(true);
3825
+ }, [line.text]);
3797
3826
  // Keep already-mounted markdown/disclosures when closed, but don't parse
3798
3827
  // new hidden deltas. Reopening catches up in a single render.
3799
3828
  if (expanded) snapshotRef.current = line;
@@ -4224,7 +4253,7 @@ function focusRegionForPaneKey(paneKey, override = null) {
4224
4253
  return PANE_KEY_FOCUS_REGIONS[String(paneKey || "")] || null;
4225
4254
  }
4226
4255
 
4227
- function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, lines, stickyLines = [], bottomStickyLines = [], reserveBottomSticky = false, scrollOffset = 0, scrollMode = "top", paneKey, controller, className = "", panelClassName = "", topContent = null, bottomContent = null, structuredBlocks = false, stickyBottom = false, renderBody = null, focusRegion = null, panelRef = null, ariaLive = null }) {
4256
+ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, lines, stickyLines = [], bottomStickyLines = [], reserveBottomSticky = false, scrollOffset = 0, scrollMode = "top", paneKey, controller, className = "", panelClassName = "", topContent = null, bottomContent = null, structuredBlocks = false, stickyBottom = false, historyPullEnabled = false, historySessionId = null, renderBody = null, focusRegion = null, panelRef = null, ariaLive = null }) {
4228
4257
  const themeId = useControllerSelector(controller, (state) => state.ui.themeId);
4229
4258
  const theme = getTheme(themeId);
4230
4259
  const ref = React.useRef(null);
@@ -4237,7 +4266,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
4237
4266
  }, [panelRef]);
4238
4267
  const stickyRef = React.useRef(null);
4239
4268
  const syncingHorizontalRef = React.useRef(false);
4240
- const { normalizedLines, onScroll, onTouchStart, onTouchEnd } = useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller, { stickyBottom });
4269
+ const { normalizedLines, onScroll, onTouchStart, onTouchEnd, onTouchCancel, topPullDistance, loadingOlderHistory } = useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller, { stickyBottom, historyPullEnabled, historySessionId });
4241
4270
  const normalizedSticky = React.useMemo(() => normalizeLines(stickyLines), [stickyLines]);
4242
4271
  const normalizedBottomSticky = React.useMemo(() => normalizeLines(bottomStickyLines), [bottomStickyLines]);
4243
4272
  const preserveHorizontalScroll = className.includes("is-preserve") && panelClassName.includes("has-preserved-sticky");
@@ -4326,7 +4355,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
4326
4355
  }, [preserveHorizontalScroll, syncScrollLeft]);
4327
4356
 
4328
4357
  return React.createElement(Panel, { title, titleRight, color, focused, actions, theme, className: panelClassName },
4329
- topContent,
4358
+ typeof topContent === "function" ? topContent({ topPullDistance, loadingOlderHistory }) : topContent,
4330
4359
  normalizedSticky.length > 0
4331
4360
  ? React.createElement("div", {
4332
4361
  ref: stickyRef,
@@ -4336,7 +4365,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
4336
4365
  normalizedSticky.map((line, index) => React.createElement(Line, { key: `sticky:${index}`, line, theme })),
4337
4366
  )
4338
4367
  : null,
4339
- React.createElement("div", { ref: setPanelNode, className: `ps-scroll-panel ${className}${scrollShadow.down ? " is-scrolled-down" : ""}${scrollShadow.up ? " is-scrolled-up" : ""}`.trim(), "data-session-scroll": focusRegion === "sessions" ? "1" : undefined, "aria-live": ariaLive || undefined, "aria-atomic": ariaLive ? "false" : undefined, onScroll: handleBodyScroll, onMouseDown: claimFocus, onTouchStart, onTouchEnd, onTouchCancel: onTouchEnd },
4368
+ React.createElement("div", { ref: setPanelNode, className: `ps-scroll-panel ${className}${scrollShadow.down ? " is-scrolled-down" : ""}${scrollShadow.up ? " is-scrolled-up" : ""}`.trim(), "data-session-scroll": focusRegion === "sessions" ? "1" : undefined, "aria-live": ariaLive || undefined, "aria-atomic": ariaLive ? "false" : undefined, onScroll: handleBodyScroll, onMouseDown: claimFocus, onTouchStart, onTouchEnd, onTouchCancel },
4340
4369
  typeof renderBody === "function"
4341
4370
  ? renderBody(normalizedLines, theme)
4342
4371
  : structuredBlocks
@@ -6898,6 +6927,39 @@ function SessionHeaderStatus({ controller }) {
6898
6927
  queue ? React.createElement("span", { className: "ps-mobile-queue", title: queue }, queue) : null);
6899
6928
  }
6900
6929
 
6930
+ function HistoryUpGlyph() {
6931
+ return React.createElement(Glyph, null,
6932
+ React.createElement("path", { d: "M12 19V5m-5 5 5-5 5 5" }));
6933
+ }
6934
+
6935
+ function HistorySpinnerGlyph() {
6936
+ return React.createElement(Glyph, null,
6937
+ React.createElement("path", { d: "M20.5 12a8.5 8.5 0 1 1-8.5-8.5" }));
6938
+ }
6939
+
6940
+ function HistoryLoadIndicator({ loading, manual, pullDistance, onManualLoad }) {
6941
+ const ready = pullDistance >= TOUCH_TOP_PULL_THRESHOLD_PX;
6942
+ const label = loading ? "Loading earlier messages…"
6943
+ : manual ? "Load older messages"
6944
+ : ready ? "Release to load earlier messages"
6945
+ : "Pull for earlier messages";
6946
+ return React.createElement("div", {
6947
+ className: `ps-history-load${loading ? " is-loading" : ""}${manual ? " is-manual" : ""}${ready ? " is-ready" : ""}`,
6948
+ style: pullDistance > 0 ? { "--ps-history-pull": `${Math.min(pullDistance, TOUCH_TOP_PULL_MAX_PX)}px` } : undefined,
6949
+ role: "status", "aria-live": "polite",
6950
+ },
6951
+ React.createElement("span", { className: "ps-history-track", "aria-hidden": "true" },
6952
+ React.createElement("span", { className: "ps-history-tick is-first" }),
6953
+ React.createElement("span", { className: "ps-history-tick is-second" }),
6954
+ React.createElement("span", { className: "ps-history-marker" },
6955
+ loading ? React.createElement(HistorySpinnerGlyph) : React.createElement(HistoryUpGlyph),
6956
+ loading ? React.createElement("span", { className: "ps-history-text-spinner" },
6957
+ ["|", "/", "—", "\\"].map((frame, index) => React.createElement("span", { key: index }, frame))) : null)),
6958
+ manual && !loading
6959
+ ? React.createElement("button", { type: "button", className: "ps-mini-button ps-load-older-button", onClick: onManualLoad }, label)
6960
+ : React.createElement("span", { className: "ps-history-label" }, label));
6961
+ }
6962
+
6901
6963
  function ChatPane({ controller, mobile = false, fullWidth = false, showComposer = true, onEnterZen = null, activityInHeader = false }) {
6902
6964
  const themeId = useControllerSelector(controller, (state) => state.ui.themeId);
6903
6965
  const theme = getTheme(themeId);
@@ -6980,7 +7042,7 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
6980
7042
  () => appendAnimatedDotsToRuns(chrome.titleRight, chrome.animateTitleRight ? animatedDots : ""),
6981
7043
  [animatedDots, chrome.animateTitleRight, chrome.titleRight],
6982
7044
  );
6983
- const [loadingOlder, setLoadingOlder] = React.useState(false);
7045
+ const [manualLoadingSessionId, setManualLoadingSessionId] = React.useState(null);
6984
7046
  // Scroll-up expands the transcript automatically until the soft cap, then
6985
7047
  // refuses — and the portal had no control to ask for more, so a busy
6986
7048
  // session's history became unreachable from the browser. Surface the
@@ -7079,25 +7141,27 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
7079
7141
  scrollMode: viewState.followBottom ? "bottom" : "top",
7080
7142
  stickyBottom: true,
7081
7143
  paneKey: "chat",
7144
+ historySessionId: viewState.activeSessionId,
7145
+ historyPullEnabled: Boolean(viewState.activeHistory?.hasOlderEvents && !showLoadOlder),
7082
7146
  ariaLive: "polite",
7083
7147
  className: "is-wrapped",
7084
7148
  panelClassName: "ps-chat-panel",
7085
7149
  panelRef,
7086
7150
  bottomContent: composer,
7087
- topContent: showLoadOlder
7088
- ? React.createElement("div", { className: "ps-load-older-bar" },
7089
- React.createElement("button", {
7090
- type: "button",
7091
- className: "ps-load-older-button",
7092
- disabled: loadingOlder,
7093
- onClick: () => {
7094
- setLoadingOlder(true);
7095
- controller.handleCommand(UI_COMMANDS.EXPAND_HISTORY)
7096
- .catch(() => {})
7097
- .finally(() => setLoadingOlder(false));
7098
- },
7099
- }, loadingOlder ? "Loading older messages…" : "↑ Load older messages"))
7100
- : null,
7151
+ topContent: ({ topPullDistance, loadingOlderHistory }) => {
7152
+ const loading = loadingOlderHistory || manualLoadingSessionId === viewState.activeSessionId;
7153
+ if (!showLoadOlder && topPullDistance <= 0 && !loading) return null;
7154
+ return React.createElement(HistoryLoadIndicator, {
7155
+ loading, manual: showLoadOlder, pullDistance: loading ? 0 : topPullDistance,
7156
+ onManualLoad: () => {
7157
+ const sessionId = viewState.activeSessionId;
7158
+ setManualLoadingSessionId(sessionId);
7159
+ controller.handleCommand(UI_COMMANDS.EXPAND_HISTORY)
7160
+ .catch(() => {})
7161
+ .finally(() => setManualLoadingSessionId(current => current === sessionId ? null : current));
7162
+ },
7163
+ });
7164
+ },
7101
7165
  structuredBlocks: true,
7102
7166
  renderBody: null,
7103
7167
  });
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-CVuPn9RU.js";import{p as f}from"./chunk-JWPE2WC7-DI-c4Jvn.js";import{_ as n,l as o}from"./mermaid.core-C_2W6ze2.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-C9ifcKtK.js";import"./pilotswarm-qQVUkWws.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-Dn-J2ayj.js";import{p as f}from"./chunk-JWPE2WC7-Dlgqhr3G.js";import{_ as n,l as o}from"./mermaid.core-1gBc6FeB.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-MSAXYGkZ.js";import"./pilotswarm-BvFGjArK.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-C_2W6ze2.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-1gBc6FeB.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};