pilotswarm 0.5.71 → 0.5.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/ui/react/src/web-app.js +211 -58
- package/web/dist/assets/{abnfDiagram-VRR7QNED-C1caNr4U.js → abnfDiagram-VRR7QNED-BnkQIEhH.js} +1 -1
- package/web/dist/assets/{arc-BufdFJYK.js → arc-CGyB8bDx.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-C0vQblUK.js → architectureDiagram-ZJ3FMSHR-DWKmcDN1.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-B6PYWI9u.js → blockDiagram-677ZJIJ3-rn5c_Stz.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-DFoaVeMm.js → c4Diagram-LMCZKHZV-DyDQ7Ifb.js} +1 -1
- package/web/dist/assets/channel-BTptNqYp.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-CxFBb-8y.js → chunk-2Q5K7J3B-C5VqT-KF.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-DO1G8AVc.js → chunk-32BRIVSS-DT-sPck4.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-spE4bKSd.js → chunk-5VM5RSS4-yOTPomtB.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-Bc_eUP5F.js → chunk-EX3LRPZG-DmFxJ0dk.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-CGM2GAMX.js → chunk-JWPE2WC7-pjfQBm7p.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-CHdrVT1z.js → chunk-MOJQB5TN-CHa4SOoq.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-BWJK7-AW.js → chunk-RYQCIY6F-DgRDTwUy.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-D8tgpQwK.js → chunk-V7JOEXUC-BdxruVNZ.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-BP86JfVu.js → chunk-VR4S4FIN-DVyRDA0m.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-Ctq_LUrs.js → chunk-XXDRQBXY-C_SZSWHx.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-D_z8OghZ.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-D_z8OghZ.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-DNvC4thT.js → cose-bilkent-JH36ORCC-B4ttgAJ8.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-3zVj2GvI.js → cynefin-VYW2F7L2-B8cRqvMz.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-DQbITfsJ.js → cynefinDiagram-TSTJHNR4-CEubmFl-.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-BkGAcDyj.js → dagre-VKFMJZFB-CgRi7BXz.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-D3lsgelm.js → diagram-FQU43EPY-DfLMLNvB.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-C2rO9e3d.js → diagram-G47NLZAW-DMBnhSjJ.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-DJBwtT8q.js → diagram-NH7WQ7WH-CsVx_EMq.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-C-SAwEHZ.js → diagram-OA4YK3LP-Cd8z9ubn.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-BpXMlmKz.js → diagram-WEI45ONY-BIjozLvB.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-DwxUgEmE.js → ebnfDiagram-CCIWWBDH-BvEyKaUx.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-DNZMCmYz.js → erDiagram-Q63AITRT-C1KyyEXS.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-CUlGtnWV.js → flowDiagram-23GEKE2U-8vv4Bw3F.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-DCj6zkyt.js → ganttDiagram-NO4QXBWP-BQXnAu0k.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-Q6UP_dgi.js → gitGraphDiagram-IHSO6WYX-BGtXqY1X.js} +1 -1
- package/web/dist/assets/{index-23y0f1it.js → index-BixB0dRp.js} +2 -2
- package/web/dist/assets/index-CSMHeAhZ.css +1 -0
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-Cwn8XNQT.js → infoDiagram-FWYZ7A6U-CVJezXGw.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-BaMvGBAo.js → ishikawaDiagram-FXEZZL3T-D_Fi5Rxo.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-DK95FJ0t.js → journeyDiagram-5HDEW3XC-Di1In_FH.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-C6W6RJLm.js → kanban-definition-HUTT4EX6-ozcJ7MQ3.js} +1 -1
- package/web/dist/assets/{linear-Un37Oyxr.js → linear-BCHWKN_x.js} +1 -1
- package/web/dist/assets/{mermaid.core-i8uqvLPa.js → mermaid.core-DPy_PtTG.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-d_cgq1KW.js → mindmap-definition-LN4V7U3C-BUPXSGdc.js} +1 -1
- package/web/dist/assets/{papaparse.min-BiJEkQ8f.js → papaparse.min-DLMQN21E.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-5QRWM5AP.js → pegDiagram-2B236MQR-B0VRzHj8.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-VIZXsjFV.js → pieDiagram-ENE6RG2P-DJDr0spg.js} +1 -1
- package/web/dist/assets/pilotswarm-BchtZfEr.js +109 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-DMOx841b.js → quadrantDiagram-ABIIQ3AL-D80hp41u.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-lTWzjdcd.js → railroadDiagram-RFXS5EU6-BVZxxDDn.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-BE5SpiNy.js → requirementDiagram-TGXJPOKE-BYBz1YgE.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-C3LYWQTA.js → sankeyDiagram-HTMAVEWB-Cm0EjuqP.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-BTCp88tQ.js → sequenceDiagram-DBY2YBRQ-C08lIz44.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-Ppzj8n5l.js → sizeCapture-X5ZJPWSS-xEVPLUiU.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-Ch_iZ_cQ.js → stateDiagram-2N3HPSRC-C54U2MIf.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CQXFWqsn.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-BX4-Oeby.js → swimlanes-5IMT3BWC-DRPOWaFR.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BuWCDUQa.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-B1l-6l6u.js → timeline-definition-FHXFAJF6-BUhUcJ_i.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-Grpa6wFZ.js → vennDiagram-L72KCM5P-DFFa7JWQ.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-B5bXnFlL.js → wardleyDiagram-EHGQE667-Bjv--ELM.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-BkELQtgc.js → xychartDiagram-FW5EYKEG-BnGbWQJK.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/channel-CDzPwSI9.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-DdcABn-U.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-DdcABn-U.js +0 -1
- package/web/dist/assets/index-BXR-II2L.css +0 -1
- package/web/dist/assets/pilotswarm-D7Q48waO.js +0 -109
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-BbJN7t8_.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-0UA1VebA.js +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pilotswarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.73",
|
|
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.
|
|
84
|
+
"pilotswarm-sdk": "0.5.73",
|
|
85
85
|
"react": "^19.2.4",
|
|
86
86
|
"react-dom": "^19.2.4",
|
|
87
87
|
"ws": "^8.18.2"
|
package/ui/react/src/web-app.js
CHANGED
|
@@ -89,6 +89,7 @@ const PORTAL_MIN_CHAT_COLUMN_PX = 224;
|
|
|
89
89
|
const PORTAL_SESSION_COMPACT_COLUMN_PX = 190;
|
|
90
90
|
const PORTAL_SESSION_HIDDEN_COLUMN_PX = 48;
|
|
91
91
|
const SCROLL_ROW_HEIGHT = 16;
|
|
92
|
+
const HISTORY_SCROLL_GESTURE_GAP_MS = 200;
|
|
92
93
|
const SCROLL_BOTTOM_EPSILON_PX = 0.5;
|
|
93
94
|
const PROGRAMMATIC_SCROLL_TOLERANCE_PX = SCROLL_BOTTOM_EPSILON_PX;
|
|
94
95
|
// Minimum downward finger travel (px) while at the top of the chat pane before
|
|
@@ -1119,6 +1120,8 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1119
1120
|
scrollOffset,
|
|
1120
1121
|
});
|
|
1121
1122
|
const previousContentRef = React.useRef(null);
|
|
1123
|
+
const historyScrollRef = React.useRef(null);
|
|
1124
|
+
React.useEffect(() => () => { historyScrollRef.current = null; }, []);
|
|
1122
1125
|
// Touch scrolling relies on native momentum for speed: a hard flick keeps
|
|
1123
1126
|
// scrolling long after the finger lifts. Re-asserting scrollTop from state
|
|
1124
1127
|
// on every render (live events, status updates) kills that momentum at the
|
|
@@ -1151,6 +1154,31 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1151
1154
|
const firstLine = scrollBoundaryIdentity(normalizedLines);
|
|
1152
1155
|
const lastLine = scrollBoundaryIdentity(normalizedLines, true);
|
|
1153
1156
|
const previousContent = previousContentRef.current;
|
|
1157
|
+
let historyScroll = historyScrollRef.current;
|
|
1158
|
+
const activeSessionId = controller.getState().sessions.activeSessionId;
|
|
1159
|
+
if (historyScroll && historyScroll.sessionId !== activeSessionId) {
|
|
1160
|
+
historyScrollRef.current = null;
|
|
1161
|
+
historyScroll = null;
|
|
1162
|
+
}
|
|
1163
|
+
const oldestSeq = controller.getState().history.bySessionId.get(activeSessionId)?.events?.[0]?.seq;
|
|
1164
|
+
const pageArrived = historyScroll && !historyScroll.applied && oldestSeq < historyScroll.oldestSeq;
|
|
1165
|
+
let pageScrollTop = null;
|
|
1166
|
+
if (pageArrived) {
|
|
1167
|
+
// Measure the same visible block, not the total transcript height:
|
|
1168
|
+
// live content can also grow at the bottom while a page is loading.
|
|
1169
|
+
const anchor = historyScroll.anchor;
|
|
1170
|
+
const element = anchor?.element.isConnected && anchor.element.textContent === anchor.text
|
|
1171
|
+
? anchor.element
|
|
1172
|
+
: [...node.children].find(child => child.textContent === anchor?.text);
|
|
1173
|
+
const delta = element
|
|
1174
|
+
? element.getBoundingClientRect().top - anchor.top
|
|
1175
|
+
: node.scrollHeight - historyScroll.height;
|
|
1176
|
+
pageScrollTop = Math.max(0, Math.min(node.scrollHeight - node.clientHeight,
|
|
1177
|
+
node.scrollTop + delta - SCROLL_ROW_HEIGHT));
|
|
1178
|
+
historyScroll.applied = true;
|
|
1179
|
+
historyScroll.target = pageScrollTop;
|
|
1180
|
+
historyScroll.releaseAt = performance.now() + HISTORY_SCROLL_GESTURE_GAP_MS;
|
|
1181
|
+
}
|
|
1154
1182
|
const interaction = userScrollRef.current;
|
|
1155
1183
|
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1156
1184
|
const interacting = interaction.touching
|
|
@@ -1165,13 +1193,13 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1165
1193
|
// state normally preserves scrollTop, which would push the same visible
|
|
1166
1194
|
// message down by the inserted height. Compensate with the measured DOM
|
|
1167
1195
|
// height delta. Appended live text keeps the old scrollTop unchanged.
|
|
1168
|
-
const prependedWhilePaused = stickyBottom
|
|
1196
|
+
const prependedWhilePaused = !historyScroll && stickyBottom
|
|
1169
1197
|
&& previousContent?.lastLine === lastLine
|
|
1170
1198
|
&& previousContent?.firstLine !== firstLine
|
|
1171
1199
|
&& node.scrollHeight > previousContent.scrollHeight
|
|
1172
1200
|
&& (preservePausedStickyScroll || node.scrollTop <= PROGRAMMATIC_SCROLL_TOLERANCE_PX);
|
|
1173
1201
|
if (
|
|
1174
|
-
!prependedWhilePaused
|
|
1202
|
+
!pageArrived && !prependedWhilePaused
|
|
1175
1203
|
&& interacting
|
|
1176
1204
|
&& scrollMode === previousViewportState?.scrollMode
|
|
1177
1205
|
&& (scrollOffset === previousViewportState?.scrollOffset || isEchoOffset)
|
|
@@ -1180,7 +1208,7 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1180
1208
|
previousContentRef.current = { firstLine, lastLine, scrollHeight: node.scrollHeight };
|
|
1181
1209
|
return;
|
|
1182
1210
|
}
|
|
1183
|
-
const nextScrollTop = prependedWhilePaused
|
|
1211
|
+
const nextScrollTop = pageArrived ? pageScrollTop : prependedWhilePaused
|
|
1184
1212
|
? Math.max(0, Math.min(
|
|
1185
1213
|
node.scrollTop + (node.scrollHeight - previousContent.scrollHeight),
|
|
1186
1214
|
node.scrollHeight - node.clientHeight,
|
|
@@ -1198,7 +1226,7 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1198
1226
|
}
|
|
1199
1227
|
});
|
|
1200
1228
|
}
|
|
1201
|
-
if (prependedWhilePaused && paneKey && typeof controller.updatePaneScrollFromViewport === "function") {
|
|
1229
|
+
if ((pageArrived || prependedWhilePaused) && paneKey && typeof controller.updatePaneScrollFromViewport === "function") {
|
|
1202
1230
|
// The measured DOM compensation above is the new durable paused
|
|
1203
1231
|
// position. Persist it too: otherwise switching sessions and back
|
|
1204
1232
|
// reapplies the pre-page offset and moves the reader by the full
|
|
@@ -1221,6 +1249,15 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1221
1249
|
const onScroll = React.useCallback(() => {
|
|
1222
1250
|
const node = ref.current;
|
|
1223
1251
|
if (!node || !paneKey) return;
|
|
1252
|
+
const historyScroll = historyScrollRef.current;
|
|
1253
|
+
if (historyScroll?.applied && performance.now() <= historyScroll.releaseAt
|
|
1254
|
+
&& node.scrollTop < historyScroll.target) {
|
|
1255
|
+
// Native touch momentum may continue without another touchmove.
|
|
1256
|
+
node.scrollTop = historyScroll.target;
|
|
1257
|
+
historyScroll.releaseAt = performance.now() + HISTORY_SCROLL_GESTURE_GAP_MS;
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (historyScroll?.applied && performance.now() > historyScroll.releaseAt) historyScrollRef.current = null;
|
|
1224
1261
|
const pendingProgrammatic = programmaticScrollRef.current;
|
|
1225
1262
|
if (pendingProgrammatic) {
|
|
1226
1263
|
if (Math.abs(node.scrollTop - pendingProgrammatic.target) <= PROGRAMMATIC_SCROLL_TOLERANCE_PX) {
|
|
@@ -1268,13 +1305,49 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1268
1305
|
}
|
|
1269
1306
|
}, [controller, dispatchScrollOffset, paneKey, ref, scrollMode, stickyBottom]);
|
|
1270
1307
|
|
|
1308
|
+
const requestHistoryPage = React.useCallback(() => {
|
|
1309
|
+
const node = ref.current;
|
|
1310
|
+
const state = controller.getState();
|
|
1311
|
+
const sessionId = state.sessions.activeSessionId;
|
|
1312
|
+
if (!node || !sessionId || historyScrollRef.current) return;
|
|
1313
|
+
const top = node.getBoundingClientRect().top;
|
|
1314
|
+
const element = [...node.children].find(child => child.textContent?.trim() && child.getBoundingClientRect().bottom > top + 6);
|
|
1315
|
+
const pending = {
|
|
1316
|
+
sessionId, oldestSeq: state.history.bySessionId.get(sessionId)?.events?.[0]?.seq,
|
|
1317
|
+
height: node.scrollHeight, applied: false,
|
|
1318
|
+
anchor: element ? { element, text: element.textContent, top: element.getBoundingClientRect().top } : null,
|
|
1319
|
+
};
|
|
1320
|
+
historyScrollRef.current = pending;
|
|
1321
|
+
const load = controller.handleChatTopHistoryScrollIntent?.(
|
|
1322
|
+
(node.scrollHeight - node.clientHeight) / SCROLL_ROW_HEIGHT,
|
|
1323
|
+
{ force: true, preserveDomAnchor: true },
|
|
1324
|
+
);
|
|
1325
|
+
Promise.resolve(load).catch(() => {}).finally(() => {
|
|
1326
|
+
if (historyScrollRef.current !== pending) return;
|
|
1327
|
+
const oldest = controller.getState().history.bySessionId.get(sessionId)?.events?.[0]?.seq;
|
|
1328
|
+
if (!(oldest < pending.oldestSeq)) historyScrollRef.current = null;
|
|
1329
|
+
else setViewportRevision(revision => revision + 1);
|
|
1330
|
+
});
|
|
1331
|
+
}, [controller, ref]);
|
|
1332
|
+
|
|
1271
1333
|
const onWheel = React.useCallback((event) => {
|
|
1272
1334
|
const node = ref.current;
|
|
1273
|
-
if (!node || paneKey !== "chat" || event.deltaY
|
|
1335
|
+
if (!node || paneKey !== "chat" || !event.deltaY || event.ctrlKey) return;
|
|
1336
|
+
const pending = historyScrollRef.current;
|
|
1337
|
+
if (pending && event.deltaY > 0) historyScrollRef.current = null;
|
|
1338
|
+
else if (pending) {
|
|
1339
|
+
if (!pending.applied || performance.now() <= pending.releaseAt) {
|
|
1340
|
+
pending.releaseAt = performance.now() + HISTORY_SCROLL_GESTURE_GAP_MS;
|
|
1341
|
+
event.preventDefault();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
historyScrollRef.current = null;
|
|
1345
|
+
}
|
|
1346
|
+
if (event.deltaY >= 0) return;
|
|
1274
1347
|
if (node.scrollTop > PROGRAMMATIC_SCROLL_TOLERANCE_PX) return;
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
}, [
|
|
1348
|
+
event.preventDefault();
|
|
1349
|
+
requestHistoryPage();
|
|
1350
|
+
}, [paneKey, ref, requestHistoryPage]);
|
|
1278
1351
|
|
|
1279
1352
|
// Touch equivalent of the wheel-at-top gesture: touch devices never fire
|
|
1280
1353
|
// wheel events, and once scrollTop sits at 0 a further swipe-down emits no
|
|
@@ -1284,6 +1357,7 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1284
1357
|
const touchPullRef = React.useRef({ startY: null, fired: false });
|
|
1285
1358
|
const onTouchStart = React.useCallback((event) => {
|
|
1286
1359
|
userScrollRef.current.touching = true;
|
|
1360
|
+
if (historyScrollRef.current?.applied) historyScrollRef.current = null;
|
|
1287
1361
|
if (paneKey !== "chat") return;
|
|
1288
1362
|
touchPullRef.current = { startY: event.touches?.[0]?.clientY ?? null, fired: false };
|
|
1289
1363
|
}, [paneKey]);
|
|
@@ -1292,26 +1366,45 @@ function useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller
|
|
|
1292
1366
|
// Momentum continues past the finger lift; onScroll keeps refreshing
|
|
1293
1367
|
// lastUserScrollAt for as long as the glide emits scroll events.
|
|
1294
1368
|
userScrollRef.current.lastUserScrollAt = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1369
|
+
if (historyScrollRef.current?.applied) {
|
|
1370
|
+
historyScrollRef.current.releaseAt = performance.now() + HISTORY_SCROLL_GESTURE_GAP_MS;
|
|
1371
|
+
}
|
|
1295
1372
|
}, []);
|
|
1296
1373
|
const onTouchMove = React.useCallback((event) => {
|
|
1297
1374
|
const node = ref.current;
|
|
1298
1375
|
const pull = touchPullRef.current;
|
|
1299
|
-
if (!node || paneKey !== "chat") return;
|
|
1376
|
+
if (!node || paneKey !== "chat" || event.touches?.length !== 1) return;
|
|
1377
|
+
if (historyScrollRef.current) {
|
|
1378
|
+
historyScrollRef.current.releaseAt = performance.now() + HISTORY_SCROLL_GESTURE_GAP_MS;
|
|
1379
|
+
event.preventDefault();
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1300
1382
|
if (pull.fired || pull.startY == null) return;
|
|
1301
1383
|
if (node.scrollTop > PROGRAMMATIC_SCROLL_TOLERANCE_PX) return;
|
|
1302
1384
|
const y = event.touches?.[0]?.clientY;
|
|
1303
1385
|
if (y == null || y - pull.startY < TOUCH_TOP_PULL_THRESHOLD_PX) return;
|
|
1304
1386
|
pull.fired = true;
|
|
1305
1387
|
// A deliberate pull at the top of the pane is an unambiguous request:
|
|
1306
|
-
//
|
|
1307
|
-
//
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
controller.handleChatTopHistoryScrollIntent?.(maxScroll / SCROLL_ROW_HEIGHT + 1, { force: true, preserveDomAnchor: true });
|
|
1312
|
-
}, [controller, paneKey, ref]);
|
|
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]);
|
|
1313
1393
|
|
|
1314
|
-
|
|
1394
|
+
React.useEffect(() => {
|
|
1395
|
+
const node = ref.current;
|
|
1396
|
+
if (!node) return;
|
|
1397
|
+
// React's delegated wheel/touch listeners are passive. These must be
|
|
1398
|
+
// cancellable so one in-flight gesture cannot consume the new page.
|
|
1399
|
+
node.addEventListener("wheel", onWheel, { passive: false });
|
|
1400
|
+
node.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
1401
|
+
return () => {
|
|
1402
|
+
node.removeEventListener("wheel", onWheel);
|
|
1403
|
+
node.removeEventListener("touchmove", onTouchMove);
|
|
1404
|
+
};
|
|
1405
|
+
}, [ref, onWheel, onTouchMove]);
|
|
1406
|
+
|
|
1407
|
+
return { normalizedLines, onScroll, onTouchStart, onTouchEnd };
|
|
1315
1408
|
}
|
|
1316
1409
|
|
|
1317
1410
|
function Runs({ runs, theme }) {
|
|
@@ -4144,7 +4237,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
|
|
|
4144
4237
|
}, [panelRef]);
|
|
4145
4238
|
const stickyRef = React.useRef(null);
|
|
4146
4239
|
const syncingHorizontalRef = React.useRef(false);
|
|
4147
|
-
const { normalizedLines, onScroll,
|
|
4240
|
+
const { normalizedLines, onScroll, onTouchStart, onTouchEnd } = useScrollSync(ref, lines, scrollOffset, scrollMode, paneKey, controller, { stickyBottom });
|
|
4148
4241
|
const normalizedSticky = React.useMemo(() => normalizeLines(stickyLines), [stickyLines]);
|
|
4149
4242
|
const normalizedBottomSticky = React.useMemo(() => normalizeLines(bottomStickyLines), [bottomStickyLines]);
|
|
4150
4243
|
const preserveHorizontalScroll = className.includes("is-preserve") && panelClassName.includes("has-preserved-sticky");
|
|
@@ -4243,7 +4336,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
|
|
|
4243
4336
|
normalizedSticky.map((line, index) => React.createElement(Line, { key: `sticky:${index}`, line, theme })),
|
|
4244
4337
|
)
|
|
4245
4338
|
: null,
|
|
4246
|
-
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,
|
|
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 },
|
|
4247
4340
|
typeof renderBody === "function"
|
|
4248
4341
|
? renderBody(normalizedLines, theme)
|
|
4249
4342
|
: structuredBlocks
|
|
@@ -4558,28 +4651,26 @@ function SessionDetailBox({ session, childCount = 0, pause = null, controller =
|
|
|
4558
4651
|
* fleet. That only holds while every prop is referentially stable — hence the
|
|
4559
4652
|
* hoisted click handler and ref setter rather than closures built per row.
|
|
4560
4653
|
*/
|
|
4561
|
-
/**
|
|
4562
|
-
|
|
4563
|
-
* pointerdown and needs `touch-action: none` to receive a move stream — which
|
|
4564
|
-
* on a touch screen also means the list can no longer be scrolled by dragging
|
|
4565
|
-
* it, so the gesture is withheld there entirely.
|
|
4566
|
-
*/
|
|
4567
|
-
function useCoarsePointer() {
|
|
4568
|
-
const query = "(pointer: coarse)";
|
|
4654
|
+
/** Subscribe to viewport or input changes without remounting the pane. */
|
|
4655
|
+
function useMediaQuery(query) {
|
|
4569
4656
|
const read = () => (typeof window !== "undefined" && typeof window.matchMedia === "function"
|
|
4570
4657
|
? window.matchMedia(query).matches
|
|
4571
4658
|
: false);
|
|
4572
|
-
const [
|
|
4659
|
+
const [matches, setMatches] = React.useState(read);
|
|
4573
4660
|
React.useEffect(() => {
|
|
4574
4661
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return undefined;
|
|
4575
4662
|
const media = window.matchMedia(query);
|
|
4576
|
-
const onChange = (event) =>
|
|
4663
|
+
const onChange = (event) => setMatches(event.matches);
|
|
4664
|
+
setMatches(media.matches);
|
|
4577
4665
|
media.addEventListener?.("change", onChange);
|
|
4578
4666
|
return () => media.removeEventListener?.("change", onChange);
|
|
4579
|
-
}, []);
|
|
4580
|
-
return
|
|
4667
|
+
}, [query]);
|
|
4668
|
+
return matches;
|
|
4581
4669
|
}
|
|
4582
4670
|
|
|
4671
|
+
// Withhold drag-to-folder on touch screens so finger drags can scroll the list.
|
|
4672
|
+
function useCoarsePointer() { return useMediaQuery("(pointer: coarse)"); }
|
|
4673
|
+
|
|
4583
4674
|
const RAIL_ORIGIN_PX = 13;
|
|
4584
4675
|
const RAIL_STEP_PX = 11;
|
|
4585
4676
|
const RAIL_WIDTH_PX = 1.5;
|
|
@@ -5052,11 +5143,10 @@ function useAxisLockedPan(ref, enabled = true) {
|
|
|
5052
5143
|
}, [ref, enabled]);
|
|
5053
5144
|
}
|
|
5054
5145
|
|
|
5055
|
-
const SessionSearchControl = React.memo(function SessionSearchControl({ query = "", onQuery, matchCount = 0, mobile = false, listRef = null }) {
|
|
5146
|
+
const SessionSearchControl = React.memo(function SessionSearchControl({ query = "", onQuery, matchCount = 0, mobile = false, expanded = false, onOpen, onClose, triggerRef, listRef = null }) {
|
|
5056
5147
|
// Keep typing local: expensive list filtering and external-store updates
|
|
5057
5148
|
// must never be part of the input's immediate render path.
|
|
5058
5149
|
const [draft, setDraft] = React.useState(query);
|
|
5059
|
-
const [mobileOpen, setMobileOpen] = React.useState(Boolean(query));
|
|
5060
5150
|
const timer = React.useRef(null);
|
|
5061
5151
|
const published = React.useRef(query);
|
|
5062
5152
|
const composing = React.useRef(false);
|
|
@@ -5075,29 +5165,24 @@ const SessionSearchControl = React.memo(function SessionSearchControl({ query =
|
|
|
5075
5165
|
cancel(); published.current = query; setDraft(query);
|
|
5076
5166
|
}, [query]);
|
|
5077
5167
|
const inputRef = React.useRef(null);
|
|
5078
|
-
React.useEffect(() => {
|
|
5079
|
-
if (query) setMobileOpen(true);
|
|
5080
|
-
}, [query]);
|
|
5081
|
-
const open = () => {
|
|
5082
|
-
setMobileOpen(true);
|
|
5083
|
-
requestAnimationFrame(() => inputRef.current?.focus({ preventScroll: true }));
|
|
5084
|
-
};
|
|
5085
5168
|
const closeOrClear = () => {
|
|
5086
5169
|
if (draft || query) { setDraft(""); publish(""); }
|
|
5087
|
-
else
|
|
5170
|
+
else onClose?.();
|
|
5088
5171
|
};
|
|
5089
5172
|
const resultLabel = draft !== query ? "Searching…" : draft
|
|
5090
5173
|
? `${matchCount} ${matchCount === 1 ? "match" : "matches"}` : "";
|
|
5091
5174
|
return React.createElement("div", {
|
|
5092
|
-
className: `ps-session-search${
|
|
5175
|
+
className: `ps-session-search${expanded ? " is-open" : ""}`,
|
|
5093
5176
|
"data-session-search": "true",
|
|
5094
5177
|
},
|
|
5095
5178
|
mobile ? React.createElement("button", {
|
|
5096
5179
|
type: "button",
|
|
5097
5180
|
className: "ps-session-search-trigger",
|
|
5181
|
+
ref: triggerRef,
|
|
5182
|
+
"aria-expanded": expanded,
|
|
5098
5183
|
"aria-label": "Search sessions",
|
|
5099
5184
|
title: "Search sessions",
|
|
5100
|
-
onClick:
|
|
5185
|
+
onClick: onOpen,
|
|
5101
5186
|
}, React.createElement(SearchGlyph)) : null,
|
|
5102
5187
|
React.createElement("div", { className: "ps-session-search-fields" },
|
|
5103
5188
|
React.createElement(SearchGlyph),
|
|
@@ -5128,7 +5213,7 @@ const SessionSearchControl = React.memo(function SessionSearchControl({ query =
|
|
|
5128
5213
|
role: "status",
|
|
5129
5214
|
"aria-live": "polite",
|
|
5130
5215
|
}, resultLabel) : null,
|
|
5131
|
-
|
|
5216
|
+
draft ? React.createElement("button", {
|
|
5132
5217
|
type: "button",
|
|
5133
5218
|
className: "ps-session-search-clear",
|
|
5134
5219
|
"aria-label": draft ? "Clear session search" : "Close session search",
|
|
@@ -5145,6 +5230,11 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
5145
5230
|
// sessions-ONLY layout is the exception: it has the whole screen and the
|
|
5146
5231
|
// detail box is the point of asking for it.
|
|
5147
5232
|
const isMobilePane = String(panelClassName).includes("ps-mobile-session-pane");
|
|
5233
|
+
const compactSearch = useMediaQuery("(max-width: 920px)");
|
|
5234
|
+
const [searchOpen, setSearchOpen] = React.useState(false);
|
|
5235
|
+
const searchOverlay = compactSearch && searchOpen;
|
|
5236
|
+
const searchDialogRef = React.useRef(null);
|
|
5237
|
+
const searchTriggerRef = React.useRef(null);
|
|
5148
5238
|
// Vertical is the primary scroll axis; sideways takes a deliberate swipe.
|
|
5149
5239
|
const sessionListRef = React.useRef(null);
|
|
5150
5240
|
// Selection reverts to plain taps wherever the primary input is a finger:
|
|
@@ -5253,6 +5343,35 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
5253
5343
|
}
|
|
5254
5344
|
searchEnvRef.current?.setQuery?.(next);
|
|
5255
5345
|
}, []);
|
|
5346
|
+
const closeSearch = React.useCallback(() => {
|
|
5347
|
+
setSearchOpen(false);
|
|
5348
|
+
setSearchQuery("");
|
|
5349
|
+
requestAnimationFrame(() => searchTriggerRef.current?.focus({ preventScroll: true }));
|
|
5350
|
+
}, [setSearchQuery]);
|
|
5351
|
+
React.useLayoutEffect(() => {
|
|
5352
|
+
const dialog = searchDialogRef.current;
|
|
5353
|
+
if (!searchOverlay || !dialog) return undefined;
|
|
5354
|
+
const viewport = window.visualViewport;
|
|
5355
|
+
const resize = () => {
|
|
5356
|
+
dialog.style.top = `${(viewport?.offsetTop || 0) + 8}px`;
|
|
5357
|
+
dialog.style.left = `${(viewport?.offsetLeft || 0) + 8}px`;
|
|
5358
|
+
dialog.style.width = `${(viewport?.width || window.innerWidth) - 16}px`;
|
|
5359
|
+
dialog.style.height = `${(viewport?.height || window.innerHeight) - 16}px`;
|
|
5360
|
+
};
|
|
5361
|
+
resize();
|
|
5362
|
+
dialog.showModal();
|
|
5363
|
+
dialog.querySelector("input")?.focus({ preventScroll: true });
|
|
5364
|
+
viewport?.addEventListener("resize", resize);
|
|
5365
|
+
viewport?.addEventListener("scroll", resize);
|
|
5366
|
+
window.addEventListener("resize", resize);
|
|
5367
|
+
return () => {
|
|
5368
|
+
viewport?.removeEventListener("resize", resize);
|
|
5369
|
+
viewport?.removeEventListener("scroll", resize);
|
|
5370
|
+
window.removeEventListener("resize", resize);
|
|
5371
|
+
dialog.close();
|
|
5372
|
+
};
|
|
5373
|
+
}, [searchOverlay]);
|
|
5374
|
+
React.useEffect(() => { if (!compactSearch) setSearchOpen(false); }, [compactSearch]);
|
|
5256
5375
|
React.useLayoutEffect(() => {
|
|
5257
5376
|
if (viewState.filterQuery || !searchScrollRef.current.restore || !sessionListRef.current) return;
|
|
5258
5377
|
sessionListRef.current.scrollTop = searchScrollRef.current.top;
|
|
@@ -5386,7 +5505,7 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
5386
5505
|
// rows/viewState from a ref that is refreshed on every render instead of
|
|
5387
5506
|
// closing over them.
|
|
5388
5507
|
const clickEnv = React.useRef(null);
|
|
5389
|
-
clickEnv.current = { rows, viewState, controller, selection };
|
|
5508
|
+
clickEnv.current = { rows, viewState, controller, selection, searchOverlay, closeSearch };
|
|
5390
5509
|
// ── Drag sessions into / out of groups ────────────────────────────
|
|
5391
5510
|
// Pointer-driven so it works from <button> rows, can render a collection
|
|
5392
5511
|
// ghost for a multi-selection, and can highlight the destination folder.
|
|
@@ -5712,6 +5831,15 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
5712
5831
|
|
|
5713
5832
|
const handleRowClick = React.useCallback((event, row) => {
|
|
5714
5833
|
const { rows: currentRows, viewState: current, controller: ctl, selection: pick } = clickEnv.current;
|
|
5834
|
+
if (clickEnv.current.searchOverlay && !row.isGroup) {
|
|
5835
|
+
if (pick) pick.onSelect(row.sessionId);
|
|
5836
|
+
else {
|
|
5837
|
+
ctl.setFocus("sessions");
|
|
5838
|
+
ctl.loadSession(row.sessionId).catch(() => {});
|
|
5839
|
+
}
|
|
5840
|
+
clickEnv.current.closeSearch();
|
|
5841
|
+
return;
|
|
5842
|
+
}
|
|
5715
5843
|
if (pick) {
|
|
5716
5844
|
if (row.isGroup || (row.hasChildren && row.active)) ctl.dispatch({ type: row.collapsed ? "sessions/expand" : "sessions/collapse", sessionId: row.sessionId });
|
|
5717
5845
|
if (!row.isGroup) pick.onSelect(row.sessionId);
|
|
@@ -5955,14 +6083,12 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
5955
6083
|
: "Release to cancel"))
|
|
5956
6084
|
: null;
|
|
5957
6085
|
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
actionsOnly ? (actionsHost ? createPortal(React.createElement("div", { className: "ps-moa-control-actions", onClick: onAction }, panelActions), actionsHost) : null) : React.createElement(Panel, {
|
|
5961
|
-
title: [{ text: "Sessions", color: "yellow", bold: true }],
|
|
6086
|
+
const sessionPanel = actionsOnly ? (actionsHost ? createPortal(React.createElement("div", { className: "ps-moa-control-actions", onClick: onAction }, panelActions), actionsHost) : null) : React.createElement(Panel, {
|
|
6087
|
+
title: [{ text: searchOverlay ? "Find a session" : "Sessions", color: "yellow", bold: true }],
|
|
5962
6088
|
color: "yellow",
|
|
5963
6089
|
focused: viewState.focused,
|
|
5964
6090
|
theme,
|
|
5965
|
-
actions: selection ? React.createElement(React.Fragment, null,
|
|
6091
|
+
actions: searchOverlay ? React.createElement(IconButton, { label: "Close session search", icon: "×", onClick: closeSearch }) : selection ? React.createElement(React.Fragment, null,
|
|
5966
6092
|
selection.onCreate ? React.createElement(IconButton, {
|
|
5967
6093
|
className: "ps-mini-button", icon: React.createElement(PlusGlyph),
|
|
5968
6094
|
label: "Create New Session", onClick: selection.onCreate,
|
|
@@ -6010,7 +6136,16 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
6010
6136
|
// touch hijacks the finger that should be scrolling the list.
|
|
6011
6137
|
drag: selection || touchInput || viewState.sortMode !== "saved" ? null : dragHandlers,
|
|
6012
6138
|
}))),
|
|
6013
|
-
React.createElement("div", {
|
|
6139
|
+
React.createElement("div", {
|
|
6140
|
+
className: "ps-session-find-controls",
|
|
6141
|
+
onPointerDown: (event) => {
|
|
6142
|
+
// Keep the search keyboard open while tapping sort, refresh, or clear.
|
|
6143
|
+
// Safari otherwise blurs the field without focusing the button.
|
|
6144
|
+
if (compactSearch && event.button === 0 && event.target.closest?.("button")
|
|
6145
|
+
&& event.currentTarget.contains(document.activeElement)
|
|
6146
|
+
&& document.activeElement?.matches("input")) event.preventDefault();
|
|
6147
|
+
},
|
|
6148
|
+
},
|
|
6014
6149
|
React.createElement("div", { className: "ps-session-sort-controls" },
|
|
6015
6150
|
React.createElement("div", { className: "ps-session-sort-modes", role: "group", "aria-label": "Session sort order" },
|
|
6016
6151
|
...[["used", "Recently used", "M12 8v4l3 2", true], ["updated", "Recently updated", "M3 12h4l3-7 4 14 3-7h4"], ["saved", "Saved order", "M6 3h12v18l-6-4-6 4Z"]].map(([mode, label, path, clock]) => React.createElement(IconButton, {
|
|
@@ -6030,10 +6165,14 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
6030
6165
|
query: viewState.filterQuery,
|
|
6031
6166
|
onQuery: setSearchQuery,
|
|
6032
6167
|
matchCount: searchMatchCount,
|
|
6033
|
-
mobile:
|
|
6168
|
+
mobile: compactSearch,
|
|
6169
|
+
expanded: searchOverlay,
|
|
6170
|
+
onOpen: () => setSearchOpen(true),
|
|
6171
|
+
onClose: closeSearch,
|
|
6172
|
+
triggerRef: searchTriggerRef,
|
|
6034
6173
|
listRef: sessionListRef,
|
|
6035
6174
|
})),
|
|
6036
|
-
(showDetailBox === null ? !isMobilePane : showDetailBox)
|
|
6175
|
+
!searchOverlay && (showDetailBox === null ? !isMobilePane : showDetailBox)
|
|
6037
6176
|
? React.createElement(SessionDetailBox, {
|
|
6038
6177
|
session: activeSession,
|
|
6039
6178
|
childCount: activeRow?.childCount || 0,
|
|
@@ -6047,7 +6186,21 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
6047
6186
|
collapsed: !detailCollapsed,
|
|
6048
6187
|
}),
|
|
6049
6188
|
})
|
|
6050
|
-
: null)
|
|
6189
|
+
: null);
|
|
6190
|
+
|
|
6191
|
+
return React.createElement(React.Fragment, null,
|
|
6192
|
+
dragGhost,
|
|
6193
|
+
!searchOverlay ? sessionPanel : null,
|
|
6194
|
+
React.createElement("dialog", {
|
|
6195
|
+
ref: searchDialogRef,
|
|
6196
|
+
className: "ps-session-search-overlay",
|
|
6197
|
+
"aria-label": "Find a session",
|
|
6198
|
+
onCancel: event => { event.preventDefault(); closeSearch(); },
|
|
6199
|
+
onKeyDownCapture: event => {
|
|
6200
|
+
if (event.key === "Escape") { event.preventDefault(); event.stopPropagation(); closeSearch(); }
|
|
6201
|
+
},
|
|
6202
|
+
onClick: event => { if (event.target === event.currentTarget) closeSearch(); },
|
|
6203
|
+
}, searchOverlay ? sessionPanel : null),
|
|
6051
6204
|
(manageOpen && activeSession && !activeSession.isGroup)
|
|
6052
6205
|
? React.createElement(SessionModifyModal, {
|
|
6053
6206
|
controller,
|
|
@@ -7225,14 +7378,14 @@ export function evaluateKeyboardTakeover(baseline, viewportWidth, viewportHeight
|
|
|
7225
7378
|
}
|
|
7226
7379
|
|
|
7227
7380
|
/**
|
|
7228
|
-
* True while the on-screen keyboard is up AND
|
|
7381
|
+
* True while the on-screen keyboard is up AND a matching control has focus.
|
|
7229
7382
|
* Both conditions matter: height alone would fire for a modal's text field
|
|
7230
7383
|
* and collapse the workspace behind the modal; focus alone would fire for
|
|
7231
7384
|
* hardware keyboards that shrink nothing. State-driven on the keyboard
|
|
7232
7385
|
* itself, so it reverts the moment the keyboard goes away — including the
|
|
7233
7386
|
* iOS swipe-dismiss that closes the keyboard without blurring the input.
|
|
7234
7387
|
*/
|
|
7235
|
-
function useKeyboardTakeover(enabled) {
|
|
7388
|
+
function useKeyboardTakeover(enabled, focusSelector = ".ps-prompt-input") {
|
|
7236
7389
|
const [takeover, setTakeover] = React.useState(false);
|
|
7237
7390
|
React.useEffect(() => {
|
|
7238
7391
|
if (!enabled || typeof window === "undefined" || !window.visualViewport) {
|
|
@@ -7244,7 +7397,7 @@ function useKeyboardTakeover(enabled) {
|
|
|
7244
7397
|
let frame = 0;
|
|
7245
7398
|
const evaluate = () => {
|
|
7246
7399
|
frame = 0;
|
|
7247
|
-
const composerFocused = Boolean(document.activeElement?.
|
|
7400
|
+
const composerFocused = Boolean(document.activeElement?.matches?.(focusSelector));
|
|
7248
7401
|
const next = evaluateKeyboardTakeover(baseline, viewport.width, viewport.height, composerFocused);
|
|
7249
7402
|
baseline = next.baseline;
|
|
7250
7403
|
setTakeover(next.takeover);
|
|
@@ -7262,7 +7415,7 @@ function useKeyboardTakeover(enabled) {
|
|
|
7262
7415
|
window.removeEventListener("focusin", schedule);
|
|
7263
7416
|
window.removeEventListener("focusout", schedule);
|
|
7264
7417
|
};
|
|
7265
|
-
}, [enabled]);
|
|
7418
|
+
}, [enabled, focusSelector]);
|
|
7266
7419
|
return enabled ? takeover : false;
|
|
7267
7420
|
}
|
|
7268
7421
|
|
package/web/dist/assets/{abnfDiagram-VRR7QNED-C1caNr4U.js → abnfDiagram-VRR7QNED-BnkQIEhH.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-CHa4SOoq.js";import{p as f}from"./chunk-JWPE2WC7-pjfQBm7p.js";import{_ as n,l as o}from"./mermaid.core-DPy_PtTG.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-B8cRqvMz.js";import"./pilotswarm-BchtZfEr.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-
|
|
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-DPy_PtTG.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};
|