pilotswarm 0.5.27 → 0.5.28
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 +1 -1
- package/ui/core/src/controller.js +17 -2
- package/ui/core/src/selectors.js +213 -7
- package/ui/react/src/web-app.js +101 -37
- package/web/dist/assets/{abnfDiagram-VRR7QNED-DOZGtiQC.js → abnfDiagram-VRR7QNED-DLV45wH4.js} +1 -1
- package/web/dist/assets/{arc-CNT314TJ.js → arc-CE4xAYdE.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-Cmi5R1em.js → architectureDiagram-ZJ3FMSHR-DfzR8-9F.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-CFQt6Pnc.js → blockDiagram-677ZJIJ3-C2pZdqvH.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-v7-eGb6Y.js → c4Diagram-LMCZKHZV-CKRspTan.js} +1 -1
- package/web/dist/assets/channel-D7p-E4El.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-DoLSc3k6.js → chunk-2Q5K7J3B-CNjLrfHT.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-l-i2NvX3.js → chunk-32BRIVSS-Bv5EwyaJ.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-B3s5kXyR.js → chunk-5VM5RSS4-NDBesEbJ.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-Ckctm_wZ.js → chunk-EX3LRPZG-DHQht9ty.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-D5reV4oK.js → chunk-JWPE2WC7-r0OQpJRF.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-B-bgmTfa.js → chunk-MOJQB5TN-fcwIDgtl.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-CLliUW3T.js → chunk-RYQCIY6F-x7XQlDO1.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-CsAyO1PL.js → chunk-V7JOEXUC-U8ZNVy1m.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-Df6VMLs3.js → chunk-VR4S4FIN-DLEdPLcN.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-pOkdkM00.js → chunk-XXDRQBXY-TeyAtB-i.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-yFZ-1yMb.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-yFZ-1yMb.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-DF9LzGIn.js → cose-bilkent-JH36ORCC-B3bAl1-Z.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-COwlV5eq.js → cynefin-VYW2F7L2-DexP8nG-.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-Cmf8ISYO.js → cynefinDiagram-TSTJHNR4-Doa2XnSs.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-DanZ5sUs.js → dagre-VKFMJZFB-DZMBKQcJ.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-xJWijzy7.js → diagram-FQU43EPY-BkzibmSq.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-C2V1yOvV.js → diagram-G47NLZAW-CihyHR3a.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-C8SrNQ_i.js → diagram-NH7WQ7WH-OMDUUxwW.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-BATn2U0p.js → diagram-OA4YK3LP-Lc0imJba.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-Dk1xkd8r.js → diagram-WEI45ONY-Dlcy8hPe.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-JFfByguj.js → ebnfDiagram-CCIWWBDH-Dh3u17Dg.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-DnlU9tGx.js → erDiagram-Q63AITRT-C0OhlUC2.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-Dnq_XrQ8.js → flowDiagram-23GEKE2U-CADKxT-x.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-MzgQ4Gkh.js → ganttDiagram-NO4QXBWP-iVVdxWB7.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-C2LwEO5a.js → gitGraphDiagram-IHSO6WYX-BwDUec_a.js} +1 -1
- package/web/dist/assets/index-BGWTu1KK.css +1 -0
- package/web/dist/assets/{index-BkjhSyx0.js → index-Djfdr7ts.js} +2 -2
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-UMiiYK4p.js → infoDiagram-FWYZ7A6U-CGPUif0s.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-CtzROVDb.js → ishikawaDiagram-FXEZZL3T-QYnrl-KA.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-C5UU06Ip.js → journeyDiagram-5HDEW3XC-CeKixxL0.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-DYwJ4PpM.js → kanban-definition-HUTT4EX6-CeYokFBS.js} +1 -1
- package/web/dist/assets/{linear-Dg7W_aPc.js → linear-BbA6_gnz.js} +1 -1
- package/web/dist/assets/{mermaid.core-OUiCmXKB.js → mermaid.core--xGRoQTa.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-ffFo5mVE.js → mindmap-definition-LN4V7U3C-D53u8h0z.js} +1 -1
- package/web/dist/assets/{papaparse.min-DPfbFiel.js → papaparse.min-BU0o7jXn.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-DJ0r7gkW.js → pegDiagram-2B236MQR-B7QSyG-3.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-BnpWDtAd.js → pieDiagram-ENE6RG2P-XtSGL7GG.js} +1 -1
- package/web/dist/assets/{pilotswarm-CU6jtK4w.js → pilotswarm-CtO819QH.js} +59 -59
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-Bcxo8lQJ.js → quadrantDiagram-ABIIQ3AL-LZoYSbTH.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-YtDm2Hhr.js → railroadDiagram-RFXS5EU6-B5FREx2U.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-DIIFSM-x.js → requirementDiagram-TGXJPOKE-1VRFkBQX.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-BH3EkrzA.js → sankeyDiagram-HTMAVEWB-J456gWvu.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-DjD5j0HL.js → sequenceDiagram-DBY2YBRQ-CaCcw6KO.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-L5FPJ9w8.js → sizeCapture-X5ZJPWSS-DClsP3_O.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-o-MgnF-b.js → stateDiagram-2N3HPSRC-D6VyrKhd.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-D7eKFhHb.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-B_7FbL2G.js → swimlanes-5IMT3BWC-q678iAuo.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-DlVHQBQx.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-C8qUL6LV.js → timeline-definition-FHXFAJF6-WnPK_zhe.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-wS-9VnAs.js → vennDiagram-L72KCM5P-WpumuLld.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-Cz8fglhX.js → wardleyDiagram-EHGQE667-CBrYLUm_.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-CmsR-UfQ.js → xychartDiagram-FW5EYKEG-DbX1rnDW.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/channel-DHDU75Fc.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-DkWfQ9TO.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-DkWfQ9TO.js +0 -1
- package/web/dist/assets/index-DyL_-2Du.css +0 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DkGZm6xe.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-D9vnXGSI.js +0 -8
package/package.json
CHANGED
|
@@ -935,7 +935,12 @@ function movePromptCursorVertically(prompt, cursor, direction) {
|
|
|
935
935
|
return getPromptCursorIndex(prompt, targetLine, position.column);
|
|
936
936
|
}
|
|
937
937
|
|
|
938
|
-
|
|
938
|
+
// Automatic scroll-up expansion stops here; past it the user must ask for more
|
|
939
|
+
// explicitly. Exported so a surface can SHOW that control — the portal had no
|
|
940
|
+
// way to trigger EXPAND_HISTORY at all, so on a busy session (tens of thousands
|
|
941
|
+
// of events) scrolling up simply died with a status line telling the user to
|
|
942
|
+
// press a key that only exists in the TUI.
|
|
943
|
+
export const AUTO_HISTORY_EVENT_SOFT_CAP = 3_000;
|
|
939
944
|
const INSPECTOR_BOTTOM_ANCHORED_TABS = new Set(["logs", "sequence"]);
|
|
940
945
|
const FILE_PREVIEW_CHAR_LIMIT = 200_000;
|
|
941
946
|
const MARKDOWN_FILE_EXTENSIONS = new Set([
|
|
@@ -6328,7 +6333,17 @@ export class PilotSwarmUiController {
|
|
|
6328
6333
|
|
|
6329
6334
|
await this.maybeAutoExpandActiveHistory(requestedScrollOffset, {
|
|
6330
6335
|
pages: AUTO_HISTORY_SCROLL_PAGE_COUNT,
|
|
6331
|
-
|
|
6336
|
+
// Always bypass the offset gate. Every caller of this method fires
|
|
6337
|
+
// only when the DOM scroller is already AT the top — a direct
|
|
6338
|
+
// measurement. The gate is a second, weaker guess that compares a
|
|
6339
|
+
// DOM-derived offset against `selectChatLines` metrics, i.e. the
|
|
6340
|
+
// TERMINAL wrapped-line count, which the rich renderer does not
|
|
6341
|
+
// use. On a session whose messages are long (tables, code blocks)
|
|
6342
|
+
// the terminal line count balloons far past the rich DOM's scroll
|
|
6343
|
+
// extent, so the gate could never open and older history was
|
|
6344
|
+
// unreachable — while a session of short messages worked fine.
|
|
6345
|
+
// The arm/fire handshake above still debounces the wheel path.
|
|
6346
|
+
force: true,
|
|
6332
6347
|
});
|
|
6333
6348
|
}
|
|
6334
6349
|
|
package/ui/core/src/selectors.js
CHANGED
|
@@ -892,6 +892,12 @@ export function selectSessionRows(state) {
|
|
|
892
892
|
entry, session, active, pinned, selected, query, ownerFilter, auth,
|
|
893
893
|
state.connection?.mode, state.sessions.selectMode,
|
|
894
894
|
totalDescendantCounts, visibleDescendantCounts,
|
|
895
|
+
// The root system row renders as the branding title, and this
|
|
896
|
+
// selector is called with two different state shapes (the portal
|
|
897
|
+
// builds a synthetic one). Without this the cache could hand a row
|
|
898
|
+
// built under one branding to a caller using another, and the root
|
|
899
|
+
// session's name would flip between them.
|
|
900
|
+
state.branding?.title,
|
|
895
901
|
];
|
|
896
902
|
const cached = memo.get(entry.sessionId);
|
|
897
903
|
if (cached && sameRowDeps(cached.deps, deps)) return cached.row;
|
|
@@ -2402,10 +2408,75 @@ export function selectLiveActivityLines(state, options = {}) {
|
|
|
2402
2408
|
return [fitRuns(runs, maxWidth)];
|
|
2403
2409
|
}
|
|
2404
2410
|
|
|
2411
|
+
// Recent selectChatLines results, compared by the IDENTITY of every input.
|
|
2412
|
+
//
|
|
2413
|
+
// This wraps the entire transcript to terminal lines, and it runs from
|
|
2414
|
+
// scrollPane / scrollPaneTo / the visual-offset helpers — i.e. on every scroll
|
|
2415
|
+
// action, to answer nothing more than "how many lines are there". Measured
|
|
2416
|
+
// 1.6ms at 300 messages and 5.1ms at 1500, per scroll.
|
|
2417
|
+
//
|
|
2418
|
+
// Keyed on identities rather than fields on purpose: selectActiveChat is NOT a
|
|
2419
|
+
// pure function of the chat array — it also appends a pending-question message,
|
|
2420
|
+
// an answered-question message and a session-error message, and branches on
|
|
2421
|
+
// ui.chatViewMode and branding. Anything derived from `session` therefore has
|
|
2422
|
+
// to invalidate when `session` does, which identity comparison gives for free.
|
|
2423
|
+
//
|
|
2424
|
+
// `byId` is in the key for two reasons found by adversarial review, both of
|
|
2425
|
+
// which produced visibly wrong output:
|
|
2426
|
+
//
|
|
2427
|
+
// 1. For a GROUP session, selectActiveChat ignores the group's own history
|
|
2428
|
+
// entirely and builds a Members table by scanning every OTHER session in
|
|
2429
|
+
// byId (title / status / shortSummary). Renaming a member changes none of
|
|
2430
|
+
// the group row's own aggregate counters, and the reducer PRESERVES a
|
|
2431
|
+
// session object's identity when nothing on it moved — so without byId the
|
|
2432
|
+
// table froze on the old titles indefinitely. (An earlier version of this
|
|
2433
|
+
// comment claimed the poll rebuilds the session object every time. It does
|
|
2434
|
+
// not, and that wrong assumption is what made the bug possible.)
|
|
2435
|
+
// 2. This memo is module-level and shared by callers that pass DIFFERENT
|
|
2436
|
+
// state shapes: the TUI's ChatPane passes a synthetic single-session byId
|
|
2437
|
+
// while the controller's scroll math passes the real full state. Every
|
|
2438
|
+
// other key field can match between them, so whichever missed first
|
|
2439
|
+
// populated one entry for both, and the member list flipped depending on
|
|
2440
|
+
// call order. Distinct byId objects now key them apart.
|
|
2441
|
+
const CHAT_LINES_MEMO_MAX = 4;
|
|
2442
|
+
let chatLinesMemo = [];
|
|
2443
|
+
|
|
2405
2444
|
export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
2445
|
+
const memoSessionId = state?.sessions?.activeSessionId ?? null;
|
|
2446
|
+
const memoKey = {
|
|
2447
|
+
sessionId: memoSessionId,
|
|
2448
|
+
session: memoSessionId ? state?.sessions?.byId?.[memoSessionId] ?? null : null,
|
|
2449
|
+
byId: state?.sessions?.byId ?? null,
|
|
2450
|
+
chat: state?.history?.bySessionId?.get?.(memoSessionId)?.chat ?? null,
|
|
2451
|
+
viewMode: state?.ui?.chatViewMode ?? null,
|
|
2452
|
+
branding: state?.branding ?? null,
|
|
2453
|
+
principal: state?.auth?.principal ?? null,
|
|
2454
|
+
maxWidth,
|
|
2455
|
+
tableMode: options?.tableMode ?? null,
|
|
2456
|
+
};
|
|
2457
|
+
for (const entry of chatLinesMemo) {
|
|
2458
|
+
if (entry.sessionId === memoKey.sessionId
|
|
2459
|
+
&& entry.session === memoKey.session
|
|
2460
|
+
&& entry.byId === memoKey.byId
|
|
2461
|
+
&& entry.chat === memoKey.chat
|
|
2462
|
+
&& entry.viewMode === memoKey.viewMode
|
|
2463
|
+
&& entry.branding === memoKey.branding
|
|
2464
|
+
&& entry.principal === memoKey.principal
|
|
2465
|
+
&& Object.is(entry.maxWidth, memoKey.maxWidth)
|
|
2466
|
+
&& entry.tableMode === memoKey.tableMode) {
|
|
2467
|
+
return entry.lines;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
const memoize = (lines) => {
|
|
2472
|
+
chatLinesMemo.unshift({ ...memoKey, lines });
|
|
2473
|
+
if (chatLinesMemo.length > CHAT_LINES_MEMO_MAX) chatLinesMemo.length = CHAT_LINES_MEMO_MAX;
|
|
2474
|
+
return lines;
|
|
2475
|
+
};
|
|
2476
|
+
|
|
2406
2477
|
const messages = selectActiveChat(state);
|
|
2407
2478
|
if (!messages || messages.length === 0) {
|
|
2408
|
-
return [{ text: "No messages yet.", color: "gray" }];
|
|
2479
|
+
return memoize([{ text: "No messages yet.", color: "gray" }]);
|
|
2409
2480
|
}
|
|
2410
2481
|
|
|
2411
2482
|
// The current viewer's identity key — a user.message whose sender differs
|
|
@@ -2449,7 +2520,7 @@ export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
|
2449
2520
|
lines.push(createBlankLine());
|
|
2450
2521
|
}
|
|
2451
2522
|
}
|
|
2452
|
-
return lines.length > 0 ? lines : [{ text: "No messages yet.", color: "gray" }];
|
|
2523
|
+
return memoize(lines.length > 0 ? lines : [{ text: "No messages yet.", color: "gray" }]);
|
|
2453
2524
|
}
|
|
2454
2525
|
|
|
2455
2526
|
// True when a chat message is plain user/assistant prose the rich (block)
|
|
@@ -2486,6 +2557,10 @@ function isRichRenderableChatMessage(message) {
|
|
|
2486
2557
|
*
|
|
2487
2558
|
* The TUI never calls this; selectChatLines remains the terminal path.
|
|
2488
2559
|
*/
|
|
2560
|
+
// Rich message blocks, cached on the message object. Keyed weakly so entries
|
|
2561
|
+
// die with the transcript they came from.
|
|
2562
|
+
const chatMessageBlockCache = new WeakMap();
|
|
2563
|
+
|
|
2489
2564
|
export function selectChatBlocks(state, maxWidth = 80, options = {}) {
|
|
2490
2565
|
const messages = selectActiveChat(state);
|
|
2491
2566
|
if (!messages || messages.length === 0) return [];
|
|
@@ -2507,6 +2582,41 @@ export function selectChatBlocks(state, maxWidth = 80, options = {}) {
|
|
|
2507
2582
|
|
|
2508
2583
|
const blocks = [];
|
|
2509
2584
|
for (const message of messages) {
|
|
2585
|
+
// Fast path FIRST. Classifying a message as rich-renderable scans its
|
|
2586
|
+
// text (asked-and-answered parsing, system-notice splitting), and that
|
|
2587
|
+
// ran for every message on every call — so a pane-splitter drag paid
|
|
2588
|
+
// it across the whole transcript per width step. A cache entry only
|
|
2589
|
+
// exists for a message already classified rich, and `kind` is part of
|
|
2590
|
+
// what is compared, so a hit is safe to take before classifying.
|
|
2591
|
+
// Object.is, NOT ===: `createdAt` is NaN whenever the source event had
|
|
2592
|
+
// no parseable timestamp, and NaN === NaN is false, so a plain equality
|
|
2593
|
+
// check could never hit for those messages — the cache would silently
|
|
2594
|
+
// do nothing while still costing a lookup. Same trap as comparing
|
|
2595
|
+
// Infinity by subtraction.
|
|
2596
|
+
const cached = message ? chatMessageBlockCache.get(message) : null;
|
|
2597
|
+
if (cached
|
|
2598
|
+
&& Object.is(cached.text, message.text)
|
|
2599
|
+
&& Object.is(cached.kind, message.kind)
|
|
2600
|
+
&& Object.is(cached.role, message.role)
|
|
2601
|
+
&& Object.is(cached.id, message.id)
|
|
2602
|
+
&& Object.is(cached.pendingPhase, message.pendingPhase)
|
|
2603
|
+
&& Object.is(cached.stopped, message.stopped)
|
|
2604
|
+
// The header is derived from these, and the chips from attachments.
|
|
2605
|
+
// Messages are mutated in place (pendingPhase provably is), so every
|
|
2606
|
+
// field the block is built from has to be compared — not just the
|
|
2607
|
+
// obvious ones.
|
|
2608
|
+
&& Object.is(cached.createdAt, message.createdAt)
|
|
2609
|
+
&& Object.is(cached.time, message.time)
|
|
2610
|
+
&& Object.is(cached.sender, message.sender)
|
|
2611
|
+
&& Object.is(cached.attachments, message.attachments)
|
|
2612
|
+
&& Object.is(cached.tableMode, buildOptions.tableMode)
|
|
2613
|
+
&& Object.is(cached.viewerKey, buildOptions.viewerKey)
|
|
2614
|
+
&& Object.is(cached.ownerKey, buildOptions.ownerKey)
|
|
2615
|
+
&& Object.is(cached.sharedContext, buildOptions.sharedContext)) {
|
|
2616
|
+
blocks.push(cached.block);
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2510
2620
|
if (message?.kind === "epoch-divider") {
|
|
2511
2621
|
blocks.push({ kind: "lines", variant: "divider", lines: [buildEpochDividerLine(message, maxWidth)] });
|
|
2512
2622
|
continue;
|
|
@@ -2520,9 +2630,15 @@ export function selectChatBlocks(state, maxWidth = 80, options = {}) {
|
|
|
2520
2630
|
continue;
|
|
2521
2631
|
}
|
|
2522
2632
|
if (isRichRenderableChatMessage(message)) {
|
|
2633
|
+
// A rich message block does NOT depend on maxWidth — the browser
|
|
2634
|
+
// wraps this text with CSS; only the terminal needs a column count.
|
|
2635
|
+
// Rebuilding it on every width step meant dragging a pane splitter
|
|
2636
|
+
// re-parsed artifact links and re-derived headers for the entire
|
|
2637
|
+
// loaded transcript to produce byte-identical output. Cache on the
|
|
2638
|
+
// message itself, keyed by the inputs that CAN change it.
|
|
2523
2639
|
const attachmentLines = buildAttachmentChipLines(message, { tableMode: "sentinel" });
|
|
2524
2640
|
const attachmentSentinel = attachmentLines.find((line) => line?.kind === "imageAttachments") || null;
|
|
2525
|
-
|
|
2641
|
+
const block = {
|
|
2526
2642
|
kind: "message",
|
|
2527
2643
|
id: message?.id ?? null,
|
|
2528
2644
|
role: message.role,
|
|
@@ -2532,17 +2648,107 @@ export function selectChatBlocks(state, maxWidth = 80, options = {}) {
|
|
|
2532
2648
|
attachments: attachmentSentinel
|
|
2533
2649
|
? { sessionId: attachmentSentinel.sessionId, attachments: attachmentSentinel.attachments }
|
|
2534
2650
|
: null,
|
|
2651
|
+
};
|
|
2652
|
+
chatMessageBlockCache.set(message, {
|
|
2653
|
+
block,
|
|
2654
|
+
text: message.text,
|
|
2655
|
+
kind: message.kind,
|
|
2656
|
+
createdAt: message.createdAt,
|
|
2657
|
+
time: message.time,
|
|
2658
|
+
sender: message.sender,
|
|
2659
|
+
attachments: message.attachments,
|
|
2660
|
+
role: message.role,
|
|
2661
|
+
id: message.id,
|
|
2662
|
+
pendingPhase: message.pendingPhase,
|
|
2663
|
+
stopped: message.stopped,
|
|
2664
|
+
tableMode: buildOptions.tableMode,
|
|
2665
|
+
viewerKey: buildOptions.viewerKey,
|
|
2666
|
+
ownerKey: buildOptions.ownerKey,
|
|
2667
|
+
sharedContext: buildOptions.sharedContext,
|
|
2535
2668
|
});
|
|
2669
|
+
blocks.push(block);
|
|
2536
2670
|
continue;
|
|
2537
2671
|
}
|
|
2538
|
-
const
|
|
2539
|
-
if (
|
|
2540
|
-
blocks.push({ kind: "lines", variant: "event", lines });
|
|
2541
|
-
}
|
|
2672
|
+
const lineBlock = cachedLineBlock(message, maxWidth, buildOptions);
|
|
2673
|
+
if (lineBlock) blocks.push(lineBlock);
|
|
2542
2674
|
}
|
|
2543
2675
|
return blocks;
|
|
2544
2676
|
}
|
|
2545
2677
|
|
|
2678
|
+
/**
|
|
2679
|
+
* Non-message blocks (thinking cards, system cards, splash, chrome-less
|
|
2680
|
+
* summaries) go through the terminal line builders, which DO depend on width —
|
|
2681
|
+
* so unlike message blocks these cannot be made width-independent. What they
|
|
2682
|
+
* can avoid is being rebuilt when the width has not moved, which is every poll:
|
|
2683
|
+
* the poll replaces `session`, the portal's useMemo sees a new selectorState,
|
|
2684
|
+
* and the whole transcript is re-derived. Measured at 1500 messages with 10%
|
|
2685
|
+
* line blocks that was 1.17ms a time, and 5.26ms at 50%.
|
|
2686
|
+
*
|
|
2687
|
+
* Only the LAST width is kept per message. A drag sweeps widths and will miss
|
|
2688
|
+
* on every step — that work is genuinely required, since these lines are
|
|
2689
|
+
* wrapped to a column count.
|
|
2690
|
+
*
|
|
2691
|
+
* The compared fields were enumerated from buildChatMessageLines and the
|
|
2692
|
+
* builders it delegates to (message/thinking/system-notice cards). `stopped` is
|
|
2693
|
+
* included because history.js sets it IN PLACE on an existing message.
|
|
2694
|
+
*/
|
|
2695
|
+
const chatLineBlockCache = new WeakMap();
|
|
2696
|
+
|
|
2697
|
+
function cachedLineBlock(message, maxWidth, buildOptions) {
|
|
2698
|
+
if (!message || typeof message !== "object") {
|
|
2699
|
+
const lines = buildChatMessageLines(message, maxWidth, buildOptions);
|
|
2700
|
+
return lines.length > 0 ? { kind: "lines", variant: "event", lines } : null;
|
|
2701
|
+
}
|
|
2702
|
+
const cached = chatLineBlockCache.get(message);
|
|
2703
|
+
if (cached
|
|
2704
|
+
&& Object.is(cached.maxWidth, maxWidth)
|
|
2705
|
+
&& Object.is(cached.text, message.text)
|
|
2706
|
+
&& Object.is(cached.mobileText, message.mobileText)
|
|
2707
|
+
&& Object.is(cached.role, message.role)
|
|
2708
|
+
&& Object.is(cached.time, message.time)
|
|
2709
|
+
&& Object.is(cached.createdAt, message.createdAt)
|
|
2710
|
+
&& Object.is(cached.splash, message.splash)
|
|
2711
|
+
&& Object.is(cached.thinking, message.thinking)
|
|
2712
|
+
&& Object.is(cached.thinkingLabel, message.thinkingLabel)
|
|
2713
|
+
&& Object.is(cached.progressKind, message.progressKind)
|
|
2714
|
+
&& Object.is(cached.noChrome, message.noChrome)
|
|
2715
|
+
&& Object.is(cached.cardTitle, message.cardTitle)
|
|
2716
|
+
&& Object.is(cached.cardTitleColor, message.cardTitleColor)
|
|
2717
|
+
&& Object.is(cached.cardBorderColor, message.cardBorderColor)
|
|
2718
|
+
&& Object.is(cached.stopped, message.stopped)
|
|
2719
|
+
&& Object.is(cached.tableMode, buildOptions.tableMode)
|
|
2720
|
+
&& Object.is(cached.viewerKey, buildOptions.viewerKey)
|
|
2721
|
+
&& Object.is(cached.ownerKey, buildOptions.ownerKey)
|
|
2722
|
+
&& Object.is(cached.sharedContext, buildOptions.sharedContext)) {
|
|
2723
|
+
return cached.block;
|
|
2724
|
+
}
|
|
2725
|
+
const lines = buildChatMessageLines(message, maxWidth, buildOptions);
|
|
2726
|
+
const block = lines.length > 0 ? { kind: "lines", variant: "event", lines } : null;
|
|
2727
|
+
chatLineBlockCache.set(message, {
|
|
2728
|
+
block,
|
|
2729
|
+
maxWidth,
|
|
2730
|
+
text: message.text,
|
|
2731
|
+
mobileText: message.mobileText,
|
|
2732
|
+
role: message.role,
|
|
2733
|
+
time: message.time,
|
|
2734
|
+
createdAt: message.createdAt,
|
|
2735
|
+
splash: message.splash,
|
|
2736
|
+
thinking: message.thinking,
|
|
2737
|
+
thinkingLabel: message.thinkingLabel,
|
|
2738
|
+
progressKind: message.progressKind,
|
|
2739
|
+
noChrome: message.noChrome,
|
|
2740
|
+
cardTitle: message.cardTitle,
|
|
2741
|
+
cardTitleColor: message.cardTitleColor,
|
|
2742
|
+
cardBorderColor: message.cardBorderColor,
|
|
2743
|
+
stopped: message.stopped,
|
|
2744
|
+
tableMode: buildOptions.tableMode,
|
|
2745
|
+
viewerKey: buildOptions.viewerKey,
|
|
2746
|
+
ownerKey: buildOptions.ownerKey,
|
|
2747
|
+
sharedContext: buildOptions.sharedContext,
|
|
2748
|
+
});
|
|
2749
|
+
return block;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2546
2752
|
// A centered inline rule ("──── label ────") for transcript markers. The dash
|
|
2547
2753
|
// runs are CAPPED (not stretched to maxWidth): the web portal wraps by pixel
|
|
2548
2754
|
// width, and a maxWidth-long "─" run overflows a narrower pane and wraps the
|
package/ui/react/src/web-app.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
computeLegacyLayout,
|
|
13
13
|
createInitialState,
|
|
14
14
|
createStore,
|
|
15
|
+
AUTO_HISTORY_EVENT_SOFT_CAP,
|
|
15
16
|
formatCompactNumber,
|
|
16
17
|
formatCronTimestampForClient,
|
|
17
18
|
formatHumanDurationSeconds,
|
|
@@ -2488,30 +2489,29 @@ function StructuredChatBlocks({ lines, theme, controller = null }) {
|
|
|
2488
2489
|
// every other block (splash, thinking cards, system cards, epoch dividers)
|
|
2489
2490
|
// reuses the structured line renderer so the terminal-era special cases
|
|
2490
2491
|
// keep exactly one implementation.
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
},
|
|
2492
|
+
/**
|
|
2493
|
+
* One rich message, memoized.
|
|
2494
|
+
*
|
|
2495
|
+
* The body runs markdown through MarkdownPreviewContent — the single most
|
|
2496
|
+
* expensive thing in the transcript — and a pane resize used to re-run it for
|
|
2497
|
+
* every loaded message. `selectChatBlocks` now returns identity-stable blocks,
|
|
2498
|
+
* so this bails out instead. Every prop must stay referentially stable for
|
|
2499
|
+
* that to hold: `theme` comes from a Map, `controller` is a singleton, and
|
|
2500
|
+
* `continued` is hoisted to a boolean because it depends on the PREVIOUS block
|
|
2501
|
+
* and would otherwise have to be recomputed inside.
|
|
2502
|
+
*/
|
|
2503
|
+
const RichChatMessage = React.memo(function RichChatMessage({ block, theme, controller, continued }) {
|
|
2504
|
+
const header = block.header || {};
|
|
2505
|
+
const roleClass = block.role === "user" ? " is-user" : " is-assistant";
|
|
2506
|
+
const pendingClass = block.pendingPhase ? ` is-${block.pendingPhase}` : "";
|
|
2507
|
+
const text = String(block.text || "");
|
|
2508
|
+
// A speaker label only earns its place when it disambiguates: another human
|
|
2509
|
+
// in a shared session. The viewer's own turns and the agent's are obvious
|
|
2510
|
+
// from side and styling.
|
|
2511
|
+
const showLabel = block.role === "user" && header.fromOtherPerson;
|
|
2512
|
+
return React.createElement("article", {
|
|
2513
|
+
className: `ps-rich-msg${roleClass}${pendingClass}${continued ? " is-continued" : ""}`,
|
|
2514
|
+
},
|
|
2515
2515
|
React.createElement("header", { className: "ps-rich-msg-head" },
|
|
2516
2516
|
showLabel
|
|
2517
2517
|
? React.createElement("span", {
|
|
@@ -2536,22 +2536,53 @@ function RichChatBlocks({ blocks, theme, controller = null }) {
|
|
|
2536
2536
|
? React.createElement("div", { className: "ps-rich-msg-body" },
|
|
2537
2537
|
React.createElement(MarkdownPreviewContent, { content: text, theme }))
|
|
2538
2538
|
: null,
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2539
|
+
block.attachments
|
|
2540
|
+
? React.createElement(ArtifactImageStrip, {
|
|
2541
|
+
controller,
|
|
2542
|
+
sessionId: block.attachments.sessionId,
|
|
2543
|
+
attachments: block.attachments.attachments,
|
|
2544
|
+
})
|
|
2545
|
+
: null);
|
|
2546
|
+
});
|
|
2547
|
+
|
|
2548
|
+
/** A non-message block (splash, system card, divider), memoized the same way. */
|
|
2549
|
+
const RichChatLineBlock = React.memo(function RichChatLineBlock({ block, theme, controller }) {
|
|
2550
|
+
const lines = React.useMemo(() => normalizeLines(block.lines || []), [block.lines]);
|
|
2551
|
+
return React.createElement("div", {
|
|
2552
|
+
className: `ps-rich-lineblock is-${block.variant || "event"}`,
|
|
2553
|
+
}, React.createElement(StructuredChatBlocks, { lines, theme, controller }));
|
|
2554
|
+
});
|
|
2555
|
+
|
|
2556
|
+
function RichChatBlocks({ blocks, theme, controller = null }) {
|
|
2557
|
+
return React.createElement("div", { className: "ps-rich-chat" },
|
|
2558
|
+
(blocks || []).map((block, index) => {
|
|
2559
|
+
if (block.kind === "message") {
|
|
2560
|
+
// Consecutive turns from the same speaker read as one
|
|
2561
|
+
// continuous passage — no repeated header, tighter spacing.
|
|
2562
|
+
// Computed here because it reads the previous block; passing it
|
|
2563
|
+
// down as a boolean is what keeps the child memoizable.
|
|
2564
|
+
const previous = blocks[index - 1];
|
|
2565
|
+
const header = block.header || {};
|
|
2566
|
+
const showLabel = block.role === "user" && header.fromOtherPerson;
|
|
2567
|
+
const continued = Boolean(previous
|
|
2568
|
+
&& previous.kind === "message"
|
|
2569
|
+
&& previous.role === block.role
|
|
2570
|
+
&& !(previous.header && previous.header.fromOtherPerson)
|
|
2571
|
+
&& !showLabel);
|
|
2572
|
+
return React.createElement(RichChatMessage, {
|
|
2573
|
+
key: block.id != null ? `msg:${block.id}` : `msg:${index}`,
|
|
2574
|
+
block,
|
|
2575
|
+
theme,
|
|
2576
|
+
controller,
|
|
2577
|
+
continued,
|
|
2578
|
+
});
|
|
2546
2579
|
}
|
|
2547
|
-
return React.createElement(
|
|
2580
|
+
return React.createElement(RichChatLineBlock, {
|
|
2548
2581
|
key: `lines:${index}`,
|
|
2549
|
-
|
|
2550
|
-
}, React.createElement(StructuredChatBlocks, {
|
|
2551
|
-
lines: normalizeLines(block.lines || []),
|
|
2582
|
+
block,
|
|
2552
2583
|
theme,
|
|
2553
2584
|
controller,
|
|
2554
|
-
})
|
|
2585
|
+
});
|
|
2555
2586
|
}));
|
|
2556
2587
|
}
|
|
2557
2588
|
|
|
@@ -3100,6 +3131,10 @@ function SessionDetailBox({ session, childCount = 0 }) {
|
|
|
3100
3131
|
: (childCount > 0 ? String(childCount) : null);
|
|
3101
3132
|
|
|
3102
3133
|
return React.createElement("div", { className: "ps-session-detail-box" },
|
|
3134
|
+
// The row above ellipsizes to one line, so this is the only place the
|
|
3135
|
+
// whole name is legible. Two lines are RESERVED whether or not they are
|
|
3136
|
+
// used, so the box height still cannot move with the selection.
|
|
3137
|
+
field("Title", session?.title, "is-title"),
|
|
3103
3138
|
field("ID", session?.sessionId, "is-id"),
|
|
3104
3139
|
field("Model", model),
|
|
3105
3140
|
field("Context", context, percent != null && percent >= 85 ? "is-hot" : percent != null && percent >= 70 ? "is-warm" : ""),
|
|
@@ -3273,6 +3308,7 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
3273
3308
|
const theme = getTheme(themeId);
|
|
3274
3309
|
const sessionButtonRefs = React.useRef(new Map());
|
|
3275
3310
|
const viewState = useControllerSelector(controller, (state) => ({
|
|
3311
|
+
branding: state.branding,
|
|
3276
3312
|
activeSessionId: state.sessions.activeSessionId,
|
|
3277
3313
|
sessionsById: state.sessions.byId,
|
|
3278
3314
|
sessionsFlat: state.sessions.flat,
|
|
@@ -3306,7 +3342,12 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
3306
3342
|
connection: {
|
|
3307
3343
|
mode: viewState.connectionMode,
|
|
3308
3344
|
},
|
|
3309
|
-
|
|
3345
|
+
// The root system session renders as the deployment's branding title.
|
|
3346
|
+
// Omitting this made the list fall back to "PilotSwarm" on a branded
|
|
3347
|
+
// deployment, while the controller's own calls (which pass real state)
|
|
3348
|
+
// produced the branded name.
|
|
3349
|
+
branding: viewState.branding,
|
|
3350
|
+
}), [viewState.activeSessionId, viewState.auth, viewState.branding, viewState.connectionMode, viewState.filterQuery, viewState.ownerFilter, viewState.pinnedIds, viewState.selectedIds, viewState.selectMode, viewState.sessionsById, viewState.sessionsFlat]);
|
|
3310
3351
|
// Hold the previous rows when a poll produced identical output.
|
|
3311
3352
|
const rows = useStableValue(computedRows);
|
|
3312
3353
|
const activeSession = viewState.activeSessionId
|
|
@@ -4044,6 +4085,15 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
|
|
|
4044
4085
|
[animatedDots, chrome.animateTitleRight, chrome.titleRight],
|
|
4045
4086
|
);
|
|
4046
4087
|
const richMode = viewState.chatViewMode === "rich" && !viewState.activeSessionIsGroup;
|
|
4088
|
+
const [loadingOlder, setLoadingOlder] = React.useState(false);
|
|
4089
|
+
// Scroll-up expands the transcript automatically until the soft cap, then
|
|
4090
|
+
// refuses — and the portal had no control to ask for more, so a busy
|
|
4091
|
+
// session's history became unreachable from the browser. Surface the
|
|
4092
|
+
// control exactly when the automatic path has given up.
|
|
4093
|
+
const showLoadOlder = Boolean(
|
|
4094
|
+
viewState.activeHistory?.hasOlderEvents
|
|
4095
|
+
&& Number(viewState.activeHistory?.loadedEventCount || 0) >= AUTO_HISTORY_EVENT_SOFT_CAP,
|
|
4096
|
+
);
|
|
4047
4097
|
const richBlocks = React.useMemo(
|
|
4048
4098
|
() => (richMode ? selectChatBlocks(selectorState, viewState.contentWidth, { tableMode: "sentinel" }) : null),
|
|
4049
4099
|
[richMode, selectorState, viewState.contentWidth],
|
|
@@ -4147,6 +4197,20 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
|
|
|
4147
4197
|
className: richMode ? "is-wrapped is-rich" : "is-wrapped",
|
|
4148
4198
|
panelClassName: richMode ? "ps-chat-panel is-rich" : "ps-chat-panel",
|
|
4149
4199
|
bottomContent: composer,
|
|
4200
|
+
topContent: showLoadOlder
|
|
4201
|
+
? React.createElement("div", { className: "ps-load-older-bar" },
|
|
4202
|
+
React.createElement("button", {
|
|
4203
|
+
type: "button",
|
|
4204
|
+
className: "ps-load-older-button",
|
|
4205
|
+
disabled: loadingOlder,
|
|
4206
|
+
onClick: () => {
|
|
4207
|
+
setLoadingOlder(true);
|
|
4208
|
+
controller.handleCommand(UI_COMMANDS.EXPAND_HISTORY)
|
|
4209
|
+
.catch(() => {})
|
|
4210
|
+
.finally(() => setLoadingOlder(false));
|
|
4211
|
+
},
|
|
4212
|
+
}, loadingOlder ? "Loading older messages…" : "↑ Load older messages"))
|
|
4213
|
+
: null,
|
|
4150
4214
|
structuredBlocks: true,
|
|
4151
4215
|
renderBody: richBlocks
|
|
4152
4216
|
? (_bodyLines, bodyTheme) => React.createElement(RichChatBlocks, {
|
package/web/dist/assets/{abnfDiagram-VRR7QNED-DOZGtiQC.js → abnfDiagram-VRR7QNED-DLV45wH4.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-fcwIDgtl.js";import{p as f}from"./chunk-JWPE2WC7-r0OQpJRF.js";import{_ as n,l as o}from"./mermaid.core--xGRoQTa.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-DexP8nG-.js";import"./pilotswarm-CtO819QH.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--xGRoQTa.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};
|