pilotswarm 0.5.26 → 0.5.27

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 (70) hide show
  1. package/package.json +1 -1
  2. package/ui/core/src/controller.js +49 -4
  3. package/ui/core/src/selectors.js +52 -6
  4. package/ui/react/src/web-app.js +426 -80
  5. package/web/dist/assets/{abnfDiagram-VRR7QNED-C4qRR7va.js → abnfDiagram-VRR7QNED-DOZGtiQC.js} +1 -1
  6. package/web/dist/assets/{arc-BQw2V2pL.js → arc-CNT314TJ.js} +1 -1
  7. package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-DMdWnLGG.js → architectureDiagram-ZJ3FMSHR-Cmi5R1em.js} +1 -1
  8. package/web/dist/assets/{blockDiagram-677ZJIJ3-DZnALw8j.js → blockDiagram-677ZJIJ3-CFQt6Pnc.js} +1 -1
  9. package/web/dist/assets/{c4Diagram-LMCZKHZV-BGGZ42iX.js → c4Diagram-LMCZKHZV-v7-eGb6Y.js} +1 -1
  10. package/web/dist/assets/channel-DHDU75Fc.js +1 -0
  11. package/web/dist/assets/{chunk-2Q5K7J3B-Dastiegd.js → chunk-2Q5K7J3B-DoLSc3k6.js} +1 -1
  12. package/web/dist/assets/{chunk-32BRIVSS-B4XexRzf.js → chunk-32BRIVSS-l-i2NvX3.js} +1 -1
  13. package/web/dist/assets/{chunk-5VM5RSS4-D66MNGCN.js → chunk-5VM5RSS4-B3s5kXyR.js} +1 -1
  14. package/web/dist/assets/{chunk-EX3LRPZG-DeqPS4Xm.js → chunk-EX3LRPZG-Ckctm_wZ.js} +1 -1
  15. package/web/dist/assets/{chunk-JWPE2WC7-DIMWiXpM.js → chunk-JWPE2WC7-D5reV4oK.js} +1 -1
  16. package/web/dist/assets/{chunk-MOJQB5TN-vTyL0qgx.js → chunk-MOJQB5TN-B-bgmTfa.js} +1 -1
  17. package/web/dist/assets/{chunk-RYQCIY6F-C7U9MKGw.js → chunk-RYQCIY6F-CLliUW3T.js} +1 -1
  18. package/web/dist/assets/{chunk-V7JOEXUC-BzPwQ7n3.js → chunk-V7JOEXUC-CsAyO1PL.js} +1 -1
  19. package/web/dist/assets/{chunk-VR4S4FIN-CmDsyK3b.js → chunk-VR4S4FIN-Df6VMLs3.js} +1 -1
  20. package/web/dist/assets/{chunk-XXDRQBXY-ZJn6BaGL.js → chunk-XXDRQBXY-pOkdkM00.js} +1 -1
  21. package/web/dist/assets/classDiagram-OUVF2IWQ-DkWfQ9TO.js +1 -0
  22. package/web/dist/assets/classDiagram-v2-EOCWNBFH-DkWfQ9TO.js +1 -0
  23. package/web/dist/assets/{cose-bilkent-JH36ORCC-CAA8JZZM.js → cose-bilkent-JH36ORCC-DF9LzGIn.js} +1 -1
  24. package/web/dist/assets/{cynefin-VYW2F7L2-B6pX-lUD.js → cynefin-VYW2F7L2-COwlV5eq.js} +1 -1
  25. package/web/dist/assets/{cynefinDiagram-TSTJHNR4-sKTHU_UH.js → cynefinDiagram-TSTJHNR4-Cmf8ISYO.js} +1 -1
  26. package/web/dist/assets/{dagre-VKFMJZFB-EuakJ578.js → dagre-VKFMJZFB-DanZ5sUs.js} +1 -1
  27. package/web/dist/assets/{diagram-FQU43EPY-DR3M_pic.js → diagram-FQU43EPY-xJWijzy7.js} +1 -1
  28. package/web/dist/assets/{diagram-G47NLZAW-BlS6yM0u.js → diagram-G47NLZAW-C2V1yOvV.js} +1 -1
  29. package/web/dist/assets/{diagram-NH7WQ7WH-CQNXe6dc.js → diagram-NH7WQ7WH-C8SrNQ_i.js} +1 -1
  30. package/web/dist/assets/{diagram-OA4YK3LP-BizBeSQo.js → diagram-OA4YK3LP-BATn2U0p.js} +1 -1
  31. package/web/dist/assets/{diagram-WEI45ONY-C-_KM6Gg.js → diagram-WEI45ONY-Dk1xkd8r.js} +1 -1
  32. package/web/dist/assets/{ebnfDiagram-CCIWWBDH-BapWEdic.js → ebnfDiagram-CCIWWBDH-JFfByguj.js} +1 -1
  33. package/web/dist/assets/{erDiagram-Q63AITRT-Btijn1Xj.js → erDiagram-Q63AITRT-DnlU9tGx.js} +1 -1
  34. package/web/dist/assets/{flowDiagram-23GEKE2U-D8zK8HkH.js → flowDiagram-23GEKE2U-Dnq_XrQ8.js} +1 -1
  35. package/web/dist/assets/{ganttDiagram-NO4QXBWP-DyjkOIMW.js → ganttDiagram-NO4QXBWP-MzgQ4Gkh.js} +1 -1
  36. package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-3r9_QeYN.js → gitGraphDiagram-IHSO6WYX-C2LwEO5a.js} +1 -1
  37. package/web/dist/assets/{index-CIt9PdC-.js → index-BkjhSyx0.js} +2 -2
  38. package/web/dist/assets/{index-CT2_MRcC.css → index-DyL_-2Du.css} +1 -1
  39. package/web/dist/assets/{infoDiagram-FWYZ7A6U-Ccd8ACUz.js → infoDiagram-FWYZ7A6U-UMiiYK4p.js} +1 -1
  40. package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-DPRUC8V2.js → ishikawaDiagram-FXEZZL3T-CtzROVDb.js} +1 -1
  41. package/web/dist/assets/{journeyDiagram-5HDEW3XC-CkvayMsL.js → journeyDiagram-5HDEW3XC-C5UU06Ip.js} +1 -1
  42. package/web/dist/assets/{kanban-definition-HUTT4EX6-CAK9Z-ha.js → kanban-definition-HUTT4EX6-DYwJ4PpM.js} +1 -1
  43. package/web/dist/assets/{linear-B-_ZjN2w.js → linear-Dg7W_aPc.js} +1 -1
  44. package/web/dist/assets/{mermaid.core-BN9c--2r.js → mermaid.core-OUiCmXKB.js} +4 -4
  45. package/web/dist/assets/{mindmap-definition-LN4V7U3C-DuD_AHRU.js → mindmap-definition-LN4V7U3C-ffFo5mVE.js} +1 -1
  46. package/web/dist/assets/papaparse.min-DPfbFiel.js +10 -0
  47. package/web/dist/assets/{pegDiagram-2B236MQR-Cc1v58KA.js → pegDiagram-2B236MQR-DJ0r7gkW.js} +1 -1
  48. package/web/dist/assets/{pieDiagram-ENE6RG2P-Bc32--ur.js → pieDiagram-ENE6RG2P-BnpWDtAd.js} +1 -1
  49. package/web/dist/assets/pilotswarm-CU6jtK4w.js +94 -0
  50. package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-DpPTc2EP.js → quadrantDiagram-ABIIQ3AL-Bcxo8lQJ.js} +1 -1
  51. package/web/dist/assets/{railroadDiagram-RFXS5EU6-DXMzwejw.js → railroadDiagram-RFXS5EU6-YtDm2Hhr.js} +1 -1
  52. package/web/dist/assets/{requirementDiagram-TGXJPOKE-B3ImAxw-.js → requirementDiagram-TGXJPOKE-DIIFSM-x.js} +1 -1
  53. package/web/dist/assets/{sankeyDiagram-HTMAVEWB-hmVMffE2.js → sankeyDiagram-HTMAVEWB-BH3EkrzA.js} +1 -1
  54. package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-D53fk6uF.js → sequenceDiagram-DBY2YBRQ-DjD5j0HL.js} +1 -1
  55. package/web/dist/assets/{sizeCapture-X5ZJPWSS-4U-I5d2g.js → sizeCapture-X5ZJPWSS-L5FPJ9w8.js} +1 -1
  56. package/web/dist/assets/{stateDiagram-2N3HPSRC-0Y5TgWhW.js → stateDiagram-2N3HPSRC-o-MgnF-b.js} +1 -1
  57. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DkGZm6xe.js +1 -0
  58. package/web/dist/assets/{swimlanes-5IMT3BWC-Umhy4qUu.js → swimlanes-5IMT3BWC-B_7FbL2G.js} +2 -2
  59. package/web/dist/assets/swimlanesDiagram-G3AALYLV-D9vnXGSI.js +8 -0
  60. package/web/dist/assets/{timeline-definition-FHXFAJF6-D5hOg3Qu.js → timeline-definition-FHXFAJF6-C8qUL6LV.js} +1 -1
  61. package/web/dist/assets/{vennDiagram-L72KCM5P-QVIcE3UD.js → vennDiagram-L72KCM5P-wS-9VnAs.js} +1 -1
  62. package/web/dist/assets/{wardleyDiagram-EHGQE667-BEj1M2cB.js → wardleyDiagram-EHGQE667-Cz8fglhX.js} +1 -1
  63. package/web/dist/assets/{xychartDiagram-FW5EYKEG-xojRIXIf.js → xychartDiagram-FW5EYKEG-CmsR-UfQ.js} +1 -1
  64. package/web/dist/index.html +3 -3
  65. package/web/dist/assets/channel-CB157dQ5.js +0 -1
  66. package/web/dist/assets/classDiagram-OUVF2IWQ-B6JwBYlv.js +0 -1
  67. package/web/dist/assets/classDiagram-v2-EOCWNBFH-B6JwBYlv.js +0 -1
  68. package/web/dist/assets/pilotswarm-BgzdDy09.js +0 -93
  69. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DqIcGfAY.js +0 -1
  70. package/web/dist/assets/swimlanesDiagram-G3AALYLV-DA9txLi6.js +0 -8
@@ -12,6 +12,9 @@ import {
12
12
  computeLegacyLayout,
13
13
  createInitialState,
14
14
  createStore,
15
+ formatCompactNumber,
16
+ formatCronTimestampForClient,
17
+ formatHumanDurationSeconds,
15
18
  getPromptInputRows,
16
19
  getTheme,
17
20
  isThemeLight,
@@ -136,6 +139,9 @@ function supportsLocalFileOpen(controller) {
136
139
  }
137
140
 
138
141
  const SESSION_LINK_COPIED_STATUS = "Session link copied to clipboard";
142
+ // Mirrors SESSION_NAV_SETTLE_MS in the controller: the access probe rides the
143
+ // same "wait for the selection to stop moving" rule as the session fetch.
144
+ const SESSION_ACCESS_SETTLE_MS = 140;
139
145
  const SESSION_LINK_PRIVATE_WARNING = "Only people with access can open this link.";
140
146
 
141
147
  function buildSessionLinkUrl(sessionId) {
@@ -1080,6 +1086,194 @@ function stripYamlFrontmatter(content) {
1080
1086
  return text.slice(3 + close.index + close[0].length);
1081
1087
  }
1082
1088
 
1089
+ const TABULAR_ARTIFACT_RE = /\.(csv|tsv)$/i;
1090
+
1091
+ function isTabularArtifact(filename, contentType) {
1092
+ if (TABULAR_ARTIFACT_RE.test(String(filename || ""))) return true;
1093
+ return /^text\/(csv|tab-separated-values)$/i.test(String(contentType || "").trim());
1094
+ }
1095
+
1096
+ /**
1097
+ * Tabular preview.
1098
+ *
1099
+ * Parsing is delegated to Papa Parse rather than split(",") because the cases
1100
+ * that break naive parsers are exactly the ones real exports contain: quoted
1101
+ * fields holding commas, embedded newlines inside a quoted cell, and escaped
1102
+ * quotes. It is loaded lazily so opening a .md never pays for the parser.
1103
+ *
1104
+ * Rendering is our own table, not a viewer library's: the preview has to
1105
+ * inherit the active theme's chrome (Win95 bevels, MS-DOS reverse video), and
1106
+ * a drop-in viewer ships its own styling that cannot participate in that.
1107
+ */
1108
+ const TABULAR_MAX_ROWS = 2000;
1109
+
1110
+ /**
1111
+ * Quote a cell for TSV the way a spreadsheet expects: only when the value
1112
+ * would otherwise break the row/column framing.
1113
+ */
1114
+ function tsvCell(value) {
1115
+ const text = String(value ?? "");
1116
+ return /[\t\n\r"]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
1117
+ }
1118
+
1119
+ function TabularPreview({ content, filename }) {
1120
+ const [parsed, setParsed] = React.useState(null);
1121
+ const wrapRef = React.useRef(null);
1122
+ const tableRef = React.useRef(null);
1123
+ const anchorCellRef = React.useRef(null);
1124
+
1125
+ React.useEffect(() => {
1126
+ let cancelled = false;
1127
+ setParsed(null);
1128
+ import("papaparse")
1129
+ .then((mod) => {
1130
+ if (cancelled) return;
1131
+ const Papa = mod.default || mod;
1132
+ const delimiter = /\.tsv$/i.test(String(filename || "")) ? "\t" : "";
1133
+ const out = Papa.parse(String(content || "").trim(), {
1134
+ delimiter,
1135
+ skipEmptyLines: "greedy",
1136
+ });
1137
+ setParsed({ rows: out.data || [], errors: out.errors || [] });
1138
+ })
1139
+ .catch(() => { if (!cancelled) setParsed({ rows: null, errors: [{ message: "parser unavailable" }] }); });
1140
+ return () => { cancelled = true; };
1141
+ }, [content, filename]);
1142
+
1143
+ // The drag can end anywhere, including outside the pane or the window.
1144
+ React.useEffect(() => {
1145
+ const release = () => { anchorCellRef.current = null; };
1146
+ window.addEventListener("mouseup", release);
1147
+ return () => window.removeEventListener("mouseup", release);
1148
+ }, []);
1149
+
1150
+ /** Select whole cells: snap both range ends to cell boundaries. */
1151
+ const selectCellSpan = React.useCallback((a, b) => {
1152
+ if (!a || !b) return;
1153
+ const selection = window.getSelection();
1154
+ if (!selection) return;
1155
+ const backwards = !!(a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_PRECEDING);
1156
+ const range = document.createRange();
1157
+ range.setStartBefore(backwards ? b : a);
1158
+ range.setEndAfter(backwards ? a : b);
1159
+ selection.removeAllRanges();
1160
+ selection.addRange(range);
1161
+ }, []);
1162
+
1163
+ const cellFrom = (event) => (event.target?.closest ? event.target.closest("td,th") : null);
1164
+
1165
+ const onMouseDown = React.useCallback((event) => {
1166
+ if (event.button !== 0) return;
1167
+ const cell = cellFrom(event);
1168
+ if (!cell) return;
1169
+ // Suppress the native character-level selection before it starts, then
1170
+ // take focus by hand (preventDefault would otherwise deny it, and the
1171
+ // wrapper needs focus for Ctrl+A to reach us).
1172
+ event.preventDefault();
1173
+ wrapRef.current?.focus();
1174
+ anchorCellRef.current = cell;
1175
+ selectCellSpan(cell, cell);
1176
+ }, [selectCellSpan]);
1177
+
1178
+ const onMouseMove = React.useCallback((event) => {
1179
+ if (!anchorCellRef.current || !(event.buttons & 1)) return;
1180
+ const cell = cellFrom(event);
1181
+ if (cell) selectCellSpan(anchorCellRef.current, cell);
1182
+ }, [selectCellSpan]);
1183
+
1184
+ // Ctrl/Cmd+A selects the TABLE, not the page. Scoped to the pane, so it only
1185
+ // applies once the table has focus.
1186
+ const onKeyDown = React.useCallback((event) => {
1187
+ if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
1188
+ if (event.key !== "a" && event.key !== "A") return;
1189
+ const table = tableRef.current;
1190
+ const selection = window.getSelection();
1191
+ if (!table || !selection) return;
1192
+ event.preventDefault();
1193
+ event.stopPropagation();
1194
+ const range = document.createRange();
1195
+ range.selectNodeContents(table);
1196
+ selection.removeAllRanges();
1197
+ selection.addRange(range);
1198
+ }, []);
1199
+
1200
+ /**
1201
+ * Emit real TSV rather than trusting each browser's table serializer, so a
1202
+ * paste into a spreadsheet lands in the right cells every time.
1203
+ */
1204
+ const onCopy = React.useCallback((event) => {
1205
+ const table = tableRef.current;
1206
+ const selection = window.getSelection();
1207
+ if (!table || !selection || selection.rangeCount === 0) return;
1208
+ const range = selection.getRangeAt(0);
1209
+ const rows = new Map();
1210
+ for (const cell of table.querySelectorAll("th,td")) {
1211
+ if (!range.intersectsNode(cell)) continue;
1212
+ const row = cell.parentElement;
1213
+ if (!rows.has(row)) rows.set(row, []);
1214
+ rows.get(row).push(tsvCell(cell.textContent));
1215
+ }
1216
+ // Nothing of the table is selected — leave the event alone.
1217
+ if (rows.size === 0) return;
1218
+ event.clipboardData.setData("text/plain", Array.from(rows.values(), (cells) => cells.join("\t")).join("\n"));
1219
+ event.preventDefault();
1220
+ }, []);
1221
+
1222
+ if (!parsed) {
1223
+ return React.createElement("div", { className: "ps-csv-status" }, "Parsing…");
1224
+ }
1225
+ // Fall back to the raw text rather than showing nothing: a file that will
1226
+ // not parse is still readable, and hiding it would be worse than plain.
1227
+ if (!parsed.rows || parsed.rows.length === 0) {
1228
+ return React.createElement("pre", { className: "ps-md-code-pre" },
1229
+ React.createElement("code", null, String(content || "")));
1230
+ }
1231
+
1232
+ const [header, ...body] = parsed.rows;
1233
+
1234
+ // Width is the WIDEST row, not the header's. A data row with more cells
1235
+ // than the header is malformed but common (trailing delimiters, a short
1236
+ // header), and papaparse reports no error for it — iterating the header
1237
+ // would silently drop those cells, which is the one thing a data viewer
1238
+ // must never do. Reduce, not Math.max(...rows): spreading tens of
1239
+ // thousands of arguments overflows the stack.
1240
+ const columnCount = body.reduce((max, row) => Math.max(max, row.length), header.length);
1241
+ const columns = Array.from({ length: columnCount }, (_, i) => header[i] ?? "");
1242
+
1243
+ // Cap the DOM. A 1 MiB CSV (the artifact ceiling) can be ~20k rows, and
1244
+ // 20k × N cells makes the pane unusable. Showing a bounded window and
1245
+ // saying so is better than hanging the tab.
1246
+ const truncated = body.length > TABULAR_MAX_ROWS;
1247
+ const visible = truncated ? body.slice(0, TABULAR_MAX_ROWS) : body;
1248
+
1249
+ // No row/column tally — it is not part of the file, and a footer under the
1250
+ // table is one more thing to avoid when copying. The only notes worth
1251
+ // showing are the ones that say the view is NOT the whole truth.
1252
+ const notes = [];
1253
+ if (truncated) notes.push(`showing first ${TABULAR_MAX_ROWS} of ${body.length} rows`);
1254
+ if (parsed.errors.length) notes.push(`${parsed.errors.length} parse warning${parsed.errors.length === 1 ? "" : "s"}`);
1255
+
1256
+ return React.createElement("div", {
1257
+ className: "ps-csv-wrap",
1258
+ ref: wrapRef,
1259
+ tabIndex: 0,
1260
+ onMouseDown,
1261
+ onMouseMove,
1262
+ onKeyDown,
1263
+ onCopy,
1264
+ },
1265
+ React.createElement("table", { className: "ps-csv-table", ref: tableRef },
1266
+ React.createElement("thead", null,
1267
+ React.createElement("tr", null,
1268
+ columns.map((cell, i) => React.createElement("th", { key: `h${i}` }, String(cell ?? ""))))),
1269
+ React.createElement("tbody", null,
1270
+ visible.map((row, r) => React.createElement("tr", { key: `r${r}` },
1271
+ columns.map((_, c) => React.createElement("td", { key: `c${c}` }, String(row[c] ?? ""))))))),
1272
+ notes.length
1273
+ ? React.createElement("div", { className: "ps-csv-status" }, notes.join(" · "))
1274
+ : null);
1275
+ }
1276
+
1083
1277
  function codeLanguageForArtifact(filename) {
1084
1278
  const ext = /\.([A-Za-z0-9]+)$/.exec(String(filename || ""));
1085
1279
  if (!ext) return null;
@@ -2837,13 +3031,130 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
2837
3031
  // with a guide rail rather than an ASCII "└" prefix.
2838
3032
  const SESSION_KIND_GLYPHS = { group: "🗂", system: "⚙", service: "⚗" };
2839
3033
 
2840
- function RichSessionRow({ row, theme }) {
3034
+ /**
3035
+ * Selected-session details, pinned to the bottom of the Sessions panel.
3036
+ *
3037
+ * This exists so the ROWS can stay one clean line each. Everything that used
3038
+ * to unfold under the selected row (and, in the rich list, under every row)
3039
+ * lives here instead, at a fixed spot the eye can return to — so switching
3040
+ * sessions no longer reflows the list.
3041
+ *
3042
+ * Web-only by construction: the TUI renders `row.detailRuns` itself and is
3043
+ * untouched by this.
3044
+ */
3045
+ const SESSION_DETAIL_NONE = "—";
3046
+
3047
+ function SessionDetailBox({ session, childCount = 0 }) {
3048
+ // EVERY field renders on EVERY selection, empty ones as an em dash. The box
3049
+ // is a fixed grid of rows, so moving through the list cannot change its
3050
+ // height — a box that grew and shrank would shove the list under the
3051
+ // cursor mid-scroll. That is also why each value is a single clamped line.
3052
+ const field = (label, value, extraClass = "") => {
3053
+ const text = (value == null || value === "") ? SESSION_DETAIL_NONE : String(value);
3054
+ const empty = text === SESSION_DETAIL_NONE;
3055
+ return React.createElement("div", {
3056
+ className: `ps-session-detail-field${extraClass ? ` ${extraClass}` : ""}${empty ? " is-none" : ""}`,
3057
+ key: label,
3058
+ },
3059
+ React.createElement("span", { className: "ps-session-detail-label" }, label),
3060
+ React.createElement("span", { className: "ps-session-detail-value", title: text }, text));
3061
+ };
3062
+
3063
+ const usage = session?.contextUsage;
3064
+ const hasUsage = usage && Number.isFinite(usage.currentTokens) && Number.isFinite(usage.tokenLimit) && usage.tokenLimit > 0;
3065
+ const percent = hasUsage ? Math.round((usage.currentTokens / usage.tokenLimit) * 100) : null;
3066
+ const context = hasUsage
3067
+ ? `${formatCompactNumber(usage.currentTokens)} / ${formatCompactNumber(usage.tokenLimit)} · ${percent}%`
3068
+ : null;
3069
+
3070
+ // "Cron vs not" is a question the list could only answer with a glyph, so
3071
+ // spell it out — an unscheduled session says "off" rather than going blank,
3072
+ // which would be indistinguishable from "unknown".
3073
+ let cron = null;
3074
+ if (session && !session.isGroup) {
3075
+ cron = "off";
3076
+ if (session.cronActive === true) {
3077
+ cron = session.cronKind === "wall-clock"
3078
+ ? `next ${formatCronTimestampForClient(session.cronNextFireAt)}`
3079
+ : typeof session.cronInterval === "number"
3080
+ ? `every ${formatHumanDurationSeconds(session.cronInterval)}`
3081
+ : "on";
3082
+ }
3083
+ }
3084
+
3085
+ const model = session?.model
3086
+ ? (session.reasoningEffort && !String(session.model).includes(String(session.reasoningEffort))
3087
+ ? `${session.model}:${session.reasoningEffort}`
3088
+ : session.model)
3089
+ : null;
3090
+
3091
+ const access = !session ? null
3092
+ : session.visibility === "shared_write" ? "shared · write"
3093
+ : session.visibility === "shared_read" ? "shared · read"
3094
+ : "private";
3095
+
3096
+ // Groups have members rather than descendants; both answer "how many are
3097
+ // under this row", so they share the field.
3098
+ const children = session?.isGroup
3099
+ ? (session.memberCount == null ? null : String(session.memberCount))
3100
+ : (childCount > 0 ? String(childCount) : null);
3101
+
3102
+ return React.createElement("div", { className: "ps-session-detail-box" },
3103
+ field("ID", session?.sessionId, "is-id"),
3104
+ field("Model", model),
3105
+ field("Context", context, percent != null && percent >= 85 ? "is-hot" : percent != null && percent >= 70 ? "is-warm" : ""),
3106
+ field("Cron", cron, session?.cronActive === true ? "is-armed" : ""),
3107
+ field("Agent", session?.agentId),
3108
+ field("Status", session?.status),
3109
+ field("Children", children),
3110
+ field("Access", access));
3111
+ }
3112
+
3113
+ /**
3114
+ * One session row, memoized.
3115
+ *
3116
+ * The list re-renders whenever the selection moves, but `selectSessionRows`
3117
+ * hands back the SAME row object for every row whose inputs did not change, so
3118
+ * memoizing here means a keypress reconciles two rows instead of the whole
3119
+ * fleet. That only holds while every prop is referentially stable — hence the
3120
+ * hoisted click handler and ref setter rather than closures built per row.
3121
+ */
3122
+ const SessionListRow = React.memo(function SessionListRow({
3123
+ row, theme, rich, structuredRows, mobile, onRowClick, setRef,
3124
+ }) {
3125
+ const ref = React.useCallback((node) => setRef(row.sessionId, node), [setRef, row.sessionId]);
3126
+ const onClick = React.useCallback((event) => onRowClick(event, row), [onRowClick, row]);
3127
+
3128
+ return React.createElement("button", {
3129
+ type: "button",
3130
+ ref,
3131
+ className: `ps-list-button ps-session-list-button${row.active ? " is-selected" : ""}${row.selected ? " is-multiselected" : ""}${row.pinned ? " is-pinned" : ""}`,
3132
+ tabIndex: row.active ? 0 : -1,
3133
+ "aria-selected": row.active ? "true" : "false",
3134
+ onClick,
3135
+ },
3136
+ React.createElement("div", {
3137
+ className: rich ? "ps-session-row-content is-rich" : "ps-line ps-session-row-content",
3138
+ style: {
3139
+ paddingInlineStart: rich
3140
+ ? `${Math.max(0, row.depth) * 14}px`
3141
+ : `${Math.max(0, row.depth) * 4}px`,
3142
+ },
3143
+ },
3144
+ rich
3145
+ ? React.createElement(RichSessionRow, { row, theme, showDetail: mobile })
3146
+ : React.createElement(SessionRowContent, { row, theme, structured: structuredRows, showInlineDetail: mobile })));
3147
+ });
3148
+
3149
+ function RichSessionRow({ row, theme, showDetail = false }) {
2841
3150
  const chrome = row.chrome;
2842
- if (!chrome) return React.createElement(SessionRowContent, { row, theme, structured: true });
3151
+ if (!chrome) return React.createElement(SessionRowContent, { row, theme, structured: true, showInlineDetail: showDetail });
2843
3152
 
2844
3153
  const kindGlyph = SESSION_KIND_GLYPHS[chrome.kind] || null;
2845
3154
  const accent = resolveColor(theme, chrome.accentColor) || undefined;
2846
- const detailRuns = Array.isArray(row.detailRuns) ? row.detailRuns : [];
3155
+ // Mobile has no room for a detail box, so it keeps the inline detail line
3156
+ // it always had. Desktop moves that content to the panel footer.
3157
+ const detailRuns = showDetail && Array.isArray(row.detailRuns) ? row.detailRuns : [];
2847
3158
 
2848
3159
  return React.createElement("div", { className: "ps-rich-session-row" },
2849
3160
  React.createElement("div", { className: "ps-rich-session-main" },
@@ -2884,16 +3195,13 @@ function RichSessionRow({ row, theme }) {
2884
3195
  style: { color: resolveColor(theme, chrome.ctx.color) || undefined },
2885
3196
  }, chrome.ctx.text)
2886
3197
  : null),
2887
- // The detail line is ALWAYS rendered, selected or not. Swapping row
2888
- // content on selection made the whole list jump; now the only things
2889
- // that reflow are expand/collapse and the highlight moving.
2890
3198
  detailRuns.length > 0
2891
3199
  ? React.createElement("div", { className: "ps-rich-session-detail" },
2892
3200
  React.createElement(Runs, { runs: detailRuns, theme }))
2893
3201
  : null);
2894
3202
  }
2895
3203
 
2896
- function SessionRowContent({ row, theme, structured = false }) {
3204
+ function SessionRowContent({ row, theme, structured = false, showInlineDetail = false }) {
2897
3205
  const hasStructuredRuns = structured && Array.isArray(row.titleRuns);
2898
3206
  if (!hasStructuredRuns) {
2899
3207
  return Array.isArray(row.runs)
@@ -2902,14 +3210,16 @@ function SessionRowContent({ row, theme, structured = false }) {
2902
3210
  }
2903
3211
 
2904
3212
  // Dense row: the title takes one line (clamped by CSS) and the context %
2905
- // is pinned to the right. The full id · time · model · ctx detail unfolds
2906
- // only under the selected row.
3213
+ // is pinned to the right. id · time · model · ctx now live in the panel's
3214
+ // detail box rather than unfolding under the row, so selecting a session
3215
+ // no longer reflows the list.
2907
3216
  const ctxRuns = Array.isArray(row.ctxRuns) ? row.ctxRuns : [];
3217
+ const hasCtx = ctxRuns.length > 0;
3218
+ // Mobile keeps the inline unfold under the selected row (no detail box).
2908
3219
  const detailRuns = Array.isArray(row.detailRuns)
2909
3220
  ? row.detailRuns
2910
3221
  : (Array.isArray(row.selectedMetaRuns) ? row.selectedMetaRuns : []);
2911
- const hasCtx = ctxRuns.length > 0;
2912
- const hasDetail = row.active && detailRuns.length > 0;
3222
+ const hasDetail = showInlineDetail && row.active && detailRuns.length > 0;
2913
3223
 
2914
3224
  return React.createElement(React.Fragment, null,
2915
3225
  React.createElement("div", { className: "ps-session-row-line" },
@@ -2956,6 +3266,9 @@ function useStableValue(value) {
2956
3266
  }
2957
3267
 
2958
3268
  function SessionPane({ controller, actions = null, panelClassName = "", structuredRows = false }) {
3269
+ // Mobile keeps its inline detail line and gets no detail box — a reserved
3270
+ // footer would eat a meaningful slice of a phone screen.
3271
+ const isMobilePane = String(panelClassName).includes("ps-mobile-session-pane");
2959
3272
  const themeId = useControllerSelector(controller, (state) => state.ui.themeId);
2960
3273
  const theme = getTheme(themeId);
2961
3274
  const sessionButtonRefs = React.useRef(new Map());
@@ -2999,6 +3312,10 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
2999
3312
  const activeSession = viewState.activeSessionId
3000
3313
  ? viewState.sessionsById[viewState.activeSessionId] || null
3001
3314
  : null;
3315
+ // The row carries the computed child badge; the raw session does not.
3316
+ const activeRow = viewState.activeSessionId
3317
+ ? rows.find((r) => r.sessionId === viewState.activeSessionId) || null
3318
+ : null;
3002
3319
  // "Manage session" combines rename, model, and sharing in one tabbed modal
3003
3320
  // (opened from the toolbar so the composer chrome stays minimal, esp. on
3004
3321
  // mobile). Rename and sharing are owner/admin-only (session:manage /
@@ -3082,6 +3399,61 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
3082
3399
  : (activeSession && !activeSession.isSystem && !activeSession.isGroup && !activeSession.parentSessionId ? [activeSession.sessionId] : []);
3083
3400
  const canMoveToGroup = groupableIds.length > 0;
3084
3401
  const combinedPanelClassName = `ps-session-pane${viewState.rich ? " is-rich" : ""}${panelClassName ? ` ${panelClassName}` : ""}`;
3402
+ // The click handler must be referentially stable or every memoized row
3403
+ // re-renders on each keypress, defeating the point. It reads the current
3404
+ // rows/viewState from a ref that is refreshed on every render instead of
3405
+ // closing over them.
3406
+ const clickEnv = React.useRef(null);
3407
+ clickEnv.current = { rows, viewState, controller };
3408
+ const handleRowClick = React.useCallback((event, row) => {
3409
+ const { rows: currentRows, viewState: current, controller: ctl } = clickEnv.current;
3410
+ // Cmd/Ctrl-click toggles multi-selection (any row).
3411
+ if (event.metaKey || event.ctrlKey) {
3412
+ event.preventDefault();
3413
+ if (!current.selectMode) {
3414
+ ctl.dispatch({ type: "sessions/selectMode", enabled: true });
3415
+ }
3416
+ ctl.dispatch({ type: "sessions/selectToggle", sessionId: row.sessionId });
3417
+ ctl.setFocus("sessions");
3418
+ return;
3419
+ }
3420
+ // Shift-click selects a contiguous range from the active row.
3421
+ if (event.shiftKey && current.activeSessionId) {
3422
+ event.preventDefault();
3423
+ const ids = currentRows.map((r) => r.sessionId);
3424
+ const startIndex = ids.indexOf(current.activeSessionId);
3425
+ const endIndex = ids.indexOf(row.sessionId);
3426
+ if (startIndex >= 0 && endIndex >= 0) {
3427
+ const [from, to] = startIndex <= endIndex
3428
+ ? [startIndex, endIndex]
3429
+ : [endIndex, startIndex];
3430
+ const range = ids.slice(from, to + 1);
3431
+ const merged = Array.from(new Set([...(current.selectedIds || []), ...range]));
3432
+ ctl.setSessionSelection(merged);
3433
+ ctl.setFocus("sessions");
3434
+ return;
3435
+ }
3436
+ }
3437
+ const shouldToggleChildren = row.hasChildren && row.active;
3438
+ if (shouldToggleChildren) {
3439
+ ctl.dispatch({
3440
+ type: row.collapsed ? "sessions/expand" : "sessions/collapse",
3441
+ sessionId: row.sessionId,
3442
+ });
3443
+ ctl.setFocus("sessions");
3444
+ return;
3445
+ }
3446
+ // A normal click on a different row clears any multi-selection and
3447
+ // switches the active session.
3448
+ if (current.selectMode) {
3449
+ ctl.dispatch({ type: "sessions/selectClear" });
3450
+ }
3451
+ ctl.setFocus("sessions");
3452
+ if (!row.active) {
3453
+ ctl.loadSession(row.sessionId).catch(() => {});
3454
+ }
3455
+ }, []);
3456
+
3085
3457
  const setSessionButtonRef = React.useCallback((sessionId, node) => {
3086
3458
  if (!sessionId) return;
3087
3459
  if (node) {
@@ -3194,74 +3566,22 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
3194
3566
  ? React.createElement("div", { className: "ps-empty-state" }, viewState.filterQuery
3195
3567
  ? `No sessions matched "@@${viewState.filterQuery}".`
3196
3568
  : "No sessions yet.")
3197
- : rows.map((row) => React.createElement("button", {
3569
+ : rows.map((row) => React.createElement(SessionListRow, {
3198
3570
  key: row.sessionId,
3199
- type: "button",
3200
- ref: (node) => setSessionButtonRef(row.sessionId, node),
3201
- className: `ps-list-button ps-session-list-button${row.active ? " is-selected" : ""}${row.selected ? " is-multiselected" : ""}${row.pinned ? " is-pinned" : ""}`,
3202
- tabIndex: row.active ? 0 : -1,
3203
- "aria-selected": row.active ? "true" : "false",
3204
- onClick: (event) => {
3205
- // Cmd/Ctrl-click toggles multi-selection (any row).
3206
- if (event.metaKey || event.ctrlKey) {
3207
- event.preventDefault();
3208
- if (!viewState.selectMode) {
3209
- controller.dispatch({ type: "sessions/selectMode", enabled: true });
3210
- }
3211
- controller.dispatch({ type: "sessions/selectToggle", sessionId: row.sessionId });
3212
- controller.setFocus("sessions");
3213
- return;
3214
- }
3215
- // Shift-click selects a contiguous range from the active row.
3216
- if (event.shiftKey && viewState.activeSessionId) {
3217
- event.preventDefault();
3218
- const ids = rows.map((r) => r.sessionId);
3219
- const startIndex = ids.indexOf(viewState.activeSessionId);
3220
- const endIndex = ids.indexOf(row.sessionId);
3221
- if (startIndex >= 0 && endIndex >= 0) {
3222
- const [from, to] = startIndex <= endIndex
3223
- ? [startIndex, endIndex]
3224
- : [endIndex, startIndex];
3225
- const range = ids.slice(from, to + 1);
3226
- const merged = Array.from(new Set([...(viewState.selectedIds || []), ...range]));
3227
- controller.setSessionSelection(merged);
3228
- controller.setFocus("sessions");
3229
- return;
3230
- }
3231
- }
3232
- const shouldToggleChildren = row.hasChildren && row.active;
3233
- if (shouldToggleChildren) {
3234
- controller.dispatch({
3235
- type: row.collapsed ? "sessions/expand" : "sessions/collapse",
3236
- sessionId: row.sessionId,
3237
- });
3238
- controller.setFocus("sessions");
3239
- return;
3240
- }
3241
- // A normal click on a different row clears any
3242
- // multi-selection and switches the active session.
3243
- if (viewState.selectMode) {
3244
- controller.dispatch({ type: "sessions/selectClear" });
3245
- }
3246
- controller.setFocus("sessions");
3247
- if (!row.active) {
3248
- controller.loadSession(row.sessionId).catch(() => {});
3249
- }
3250
- },
3251
- },
3252
- React.createElement("div", {
3253
- className: viewState.rich ? "ps-session-row-content is-rich" : "ps-line ps-session-row-content",
3254
- style: {
3255
- paddingInlineStart: viewState.rich
3256
- ? `${Math.max(0, row.depth) * 14}px`
3257
- : `${Math.max(0, row.depth) * 4}px`,
3258
- },
3259
- },
3260
- viewState.rich
3261
- ? React.createElement(RichSessionRow, { row, theme })
3262
- : React.createElement(SessionRowContent, { row, theme, structured: structuredRows })),
3263
- )),
3264
- )),
3571
+ row,
3572
+ theme,
3573
+ rich: viewState.rich,
3574
+ structuredRows,
3575
+ mobile: isMobilePane,
3576
+ onRowClick: handleRowClick,
3577
+ setRef: setSessionButtonRef,
3578
+ }))),
3579
+ isMobilePane
3580
+ ? null
3581
+ : React.createElement(SessionDetailBox, {
3582
+ session: activeSession,
3583
+ childCount: activeRow?.childCount || 0,
3584
+ })),
3265
3585
  (manageOpen && activeSession && !activeSession.isGroup)
3266
3586
  ? React.createElement(SessionModifyModal, {
3267
3587
  controller,
@@ -3385,7 +3705,19 @@ function useActiveSessionAccess(controller, activeSessionId, isGroup) {
3385
3705
  .catch(() => { if (!cancelled) setState({ access: null, loading: false }); });
3386
3706
  return () => { cancelled = true; };
3387
3707
  }, [controller, activeSessionId, isGroup]);
3388
- React.useEffect(() => reload(), [reload]);
3708
+ // Deferred for the same reason the session fetch is: this fires on every
3709
+ // activeSessionId change, and scrolling the list changes that per keypress.
3710
+ // Without the settle, moving through a list of sessions issued one access
3711
+ // round trip per row. `reload` stays immediate for callers that ask for it
3712
+ // explicitly (after a sharing change).
3713
+ React.useEffect(() => {
3714
+ let cancelInFlight;
3715
+ const timer = setTimeout(() => { cancelInFlight = reload(); }, SESSION_ACCESS_SETTLE_MS);
3716
+ return () => {
3717
+ clearTimeout(timer);
3718
+ if (typeof cancelInFlight === "function") cancelInFlight();
3719
+ };
3720
+ }, [reload]);
3389
3721
  return { access: state.access, loading: state.loading, reload };
3390
3722
  }
3391
3723
 
@@ -4171,7 +4503,21 @@ function FilesPane({ controller, focused, mobile = false, previewOnly = false, n
4171
4503
  // In the mobile overlay the browser owns scrolling outright.
4172
4504
  const previewPaneKey = nativeScroll ? null : "filePreview";
4173
4505
 
4174
- const previewPane = previewIsImage
4506
+ const previewIsTabular = previewReady
4507
+ && !filesView.previewIsBinary
4508
+ && isTabularArtifact(filesView.selectedFilename, filesView.previewContentType)
4509
+ && Boolean(filesView.previewContent);
4510
+
4511
+ const previewPane = previewIsTabular
4512
+ ? React.createElement(RenderedPreviewPanel, {
4513
+ controller, title: null, color: "cyan", focused: false, theme,
4514
+ paneKey: previewPaneKey, scrollOffset: viewState.previewScroll,
4515
+ className: "ps-csv-preview", focusRegion: previewFocusRegion,
4516
+ }, React.createElement(TabularPreview, {
4517
+ content: filesView.previewContent,
4518
+ filename: filesView.selectedFilename,
4519
+ }))
4520
+ : previewIsImage
4175
4521
  ? React.createElement(BinaryArtifactPreviewPanel, {
4176
4522
  title: null,
4177
4523
  color: "cyan",
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-vTyL0qgx.js";import{p as f}from"./chunk-JWPE2WC7-DIMWiXpM.js";import{_ as n,l as o}from"./mermaid.core-BN9c--2r.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-B6pX-lUD.js";import"./pilotswarm-BgzdDy09.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-B-bgmTfa.js";import{p as f}from"./chunk-JWPE2WC7-D5reV4oK.js";import{_ as n,l as o}from"./mermaid.core-OUiCmXKB.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-COwlV5eq.js";import"./pilotswarm-CU6jtK4w.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-BN9c--2r.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-OUiCmXKB.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};