@tangle-network/charts 0.21.15 → 0.21.16
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/README.md +2 -2
- package/dist/{flow-trace-DBJY1YAA.d.ts → flow-trace-CSy-mQ31.d.ts} +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/react/flow-waterfall.css +122 -107
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +276 -219
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -2546,6 +2546,7 @@ function flowWaterfallRows(trace, resolutionMs) {
|
|
|
2546
2546
|
row.hasChildren = kids.length > 0;
|
|
2547
2547
|
ordered_.push(row);
|
|
2548
2548
|
const marks = [];
|
|
2549
|
+
const own = row.span.promptTokens !== void 0 || row.span.completionTokens !== void 0 || row.span.cachedTokens !== void 0;
|
|
2549
2550
|
let cost = row.span.costUsd ?? 0;
|
|
2550
2551
|
let costKnown = row.span.kind !== "model" || row.span.costUsd !== void 0;
|
|
2551
2552
|
for (const child of kids) {
|
|
@@ -2556,18 +2557,15 @@ function flowWaterfallRows(trace, resolutionMs) {
|
|
|
2556
2557
|
if (childCost === void 0) {
|
|
2557
2558
|
if (child.kind === "model" || child.hasChildren) costKnown = false;
|
|
2558
2559
|
} else cost += childCost;
|
|
2559
|
-
if (child.kind !== "pipeline" && child.kind !== "waiting" && ordered(child.span))
|
|
2560
|
-
row.own.push(segmentOf(child));
|
|
2561
|
-
const counts = row.counts ??= {
|
|
2562
|
-
model: 0,
|
|
2563
|
-
tool: 0,
|
|
2564
|
-
failed: 0
|
|
2565
|
-
};
|
|
2566
|
-
if (!child.ok) counts.failed++;
|
|
2567
|
-
else if (child.kind === "model") counts.model++;
|
|
2568
|
-
else counts.tool++;
|
|
2569
|
-
}
|
|
2560
|
+
if (child.kind !== "pipeline" && child.kind !== "waiting" && ordered(child.span)) row.own.push(segmentOf(child));
|
|
2570
2561
|
}
|
|
2562
|
+
Object.assign(row, stepStats(kids));
|
|
2563
|
+
const tokens = own ? {
|
|
2564
|
+
prompt: row.span.promptTokens,
|
|
2565
|
+
completion: row.span.completionTokens,
|
|
2566
|
+
cached: row.span.cachedTokens
|
|
2567
|
+
} : sumTokens(kids);
|
|
2568
|
+
if (tokens) row.tokens = tokens;
|
|
2571
2569
|
if (row.hasChildren && costKnown) row.rollupCostUsd = cost;
|
|
2572
2570
|
if (row.hasChildren) {
|
|
2573
2571
|
row.segments = mergeSegments(marks.map((mark) => ({ ...mark })), gap);
|
|
@@ -2580,6 +2578,74 @@ function flowWaterfallRows(trace, resolutionMs) {
|
|
|
2580
2578
|
for (const row of rows) if (!row.parentId) visit(row, 0, -1);
|
|
2581
2579
|
return ordered_;
|
|
2582
2580
|
}
|
|
2581
|
+
/** What a row's direct steps add up to; nothing when it has none. */
|
|
2582
|
+
function stepStats(steps) {
|
|
2583
|
+
let counts;
|
|
2584
|
+
const modelMs = [];
|
|
2585
|
+
for (const step of steps) {
|
|
2586
|
+
if (step.kind === "pipeline" || step.kind === "waiting" || !ordered(step.span)) continue;
|
|
2587
|
+
counts ??= {
|
|
2588
|
+
model: 0,
|
|
2589
|
+
tool: 0,
|
|
2590
|
+
failed: 0,
|
|
2591
|
+
retried: 0,
|
|
2592
|
+
tools: {}
|
|
2593
|
+
};
|
|
2594
|
+
if (!step.ok) counts.failed++;
|
|
2595
|
+
if (step.retry) counts.retried++;
|
|
2596
|
+
if (step.kind === "model") {
|
|
2597
|
+
counts.model++;
|
|
2598
|
+
if (!spanRunning(step.span)) modelMs.push(step.span.endMs - step.span.startMs);
|
|
2599
|
+
} else {
|
|
2600
|
+
counts.tool++;
|
|
2601
|
+
const tool = toolOf(step.name) || step.kind;
|
|
2602
|
+
counts.tools[tool] = (counts.tools[tool] ?? 0) + 1;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
return counts ? {
|
|
2606
|
+
counts,
|
|
2607
|
+
modelMs: modelMs.sort((a, b) => a - b)
|
|
2608
|
+
} : {};
|
|
2609
|
+
}
|
|
2610
|
+
/** Several rows' steps as one: a family's members or a folded run. */
|
|
2611
|
+
function mergeStats(rows) {
|
|
2612
|
+
let counts;
|
|
2613
|
+
for (const row of rows) {
|
|
2614
|
+
if (!row.counts) continue;
|
|
2615
|
+
counts ??= {
|
|
2616
|
+
model: 0,
|
|
2617
|
+
tool: 0,
|
|
2618
|
+
failed: 0,
|
|
2619
|
+
retried: 0,
|
|
2620
|
+
tools: {}
|
|
2621
|
+
};
|
|
2622
|
+
counts.model += row.counts.model;
|
|
2623
|
+
counts.tool += row.counts.tool;
|
|
2624
|
+
counts.failed += row.counts.failed;
|
|
2625
|
+
counts.retried += row.counts.retried;
|
|
2626
|
+
for (const [tool, n] of Object.entries(row.counts.tools)) counts.tools[tool] = (counts.tools[tool] ?? 0) + n;
|
|
2627
|
+
}
|
|
2628
|
+
const tokens = sumTokens(rows);
|
|
2629
|
+
return {
|
|
2630
|
+
...counts ? {
|
|
2631
|
+
counts,
|
|
2632
|
+
modelMs: rows.flatMap((row) => row.modelMs ?? []).sort((a, b) => a - b)
|
|
2633
|
+
} : {},
|
|
2634
|
+
...tokens ? { tokens } : {}
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
function sumTokens(rows) {
|
|
2638
|
+
let out;
|
|
2639
|
+
for (const row of rows) for (const key of [
|
|
2640
|
+
"prompt",
|
|
2641
|
+
"completion",
|
|
2642
|
+
"cached"
|
|
2643
|
+
]) {
|
|
2644
|
+
const n = row.tokens?.[key];
|
|
2645
|
+
if (n !== void 0) (out ??= {})[key] = (out[key] ?? 0) + n;
|
|
2646
|
+
}
|
|
2647
|
+
return out;
|
|
2648
|
+
}
|
|
2583
2649
|
function segmentOf(row) {
|
|
2584
2650
|
return {
|
|
2585
2651
|
startMs: row.span.startMs,
|
|
@@ -2813,6 +2879,7 @@ function agentFamilies(agents, open) {
|
|
|
2813
2879
|
alt: i % 2 === 1
|
|
2814
2880
|
}))).sort((a, b) => a.startMs - b.startMs),
|
|
2815
2881
|
...known.length ? { rollupCostUsd: known.reduce((sum, cost) => sum + cost, 0) } : {},
|
|
2882
|
+
...mergeStats(members),
|
|
2816
2883
|
span: {
|
|
2817
2884
|
id,
|
|
2818
2885
|
kind: "pipeline",
|
|
@@ -2843,35 +2910,24 @@ function agentFamilies(agents, open) {
|
|
|
2843
2910
|
}
|
|
2844
2911
|
return out;
|
|
2845
2912
|
}
|
|
2846
|
-
/** The second line of an agent row: how it ended and how much it did (
|
|
2913
|
+
/** The second line of an agent row in parts: how it ended and how much it did (failed, 3 LLM calls, 412 tool calls; 6 agents, 4 failed).
|
|
2914
|
+
* The row shows the parts in order and drops the last ones whole when the line is full, never half a word. */
|
|
2847
2915
|
function agentSummary(row) {
|
|
2848
2916
|
if (row.id.startsWith("family:")) {
|
|
2849
2917
|
const failed = Number(row.span.meta?.failedAgents ?? 0);
|
|
2850
|
-
return `${row.descendants} agents
|
|
2918
|
+
return [`${row.descendants} agents`, ...failed ? [`${failed} failed`] : []];
|
|
2851
2919
|
}
|
|
2852
2920
|
if (row.id.startsWith("run:")) {
|
|
2853
2921
|
const failed = row.own.filter((mark) => mark.kind === "failed").length;
|
|
2854
|
-
return failed ? `${failed} failed` : "all done";
|
|
2922
|
+
return [failed ? `${failed} failed` : "all done"];
|
|
2855
2923
|
}
|
|
2856
|
-
const
|
|
2857
|
-
|
|
2858
|
-
tool: 0,
|
|
2859
|
-
failed: 0
|
|
2860
|
-
};
|
|
2861
|
-
const turns = counts.model + counts.failed;
|
|
2862
|
-
const tools = counts.tool;
|
|
2863
|
-
const state = row.span.outcome?.label ?? (spanRunning(row.span) ? "running" : !row.ok ? "failed" : row.kind === "pipeline" ? "" : "done");
|
|
2864
|
-
const unit = row.kind === "pipeline" ? ["turn", "turns"] : ["model call", "model calls"];
|
|
2924
|
+
const calls = row.counts?.model ?? 0;
|
|
2925
|
+
const tools = row.counts?.tool ?? 0;
|
|
2865
2926
|
return [
|
|
2866
|
-
|
|
2867
|
-
|
|
2927
|
+
row.span.outcome?.label ?? (spanRunning(row.span) ? "running" : !row.ok ? "failed" : row.kind === "pipeline" ? "" : "done"),
|
|
2928
|
+
calls ? `${calls} LLM ${calls === 1 ? "call" : "calls"}` : "",
|
|
2868
2929
|
tools ? `${tools} tool ${tools === 1 ? "call" : "calls"}` : ""
|
|
2869
|
-
].filter(Boolean)
|
|
2870
|
-
}
|
|
2871
|
-
/** The summary split where it may be cut: the tool count goes first when the line does not fit, never half a word. */
|
|
2872
|
-
function agentSummaryParts(row) {
|
|
2873
|
-
const tools = / · (\d+ tool calls?)$/.exec(agentSummary(row));
|
|
2874
|
-
return tools ? [agentSummary(row).slice(0, tools.index), tools[1]] : [agentSummary(row)];
|
|
2930
|
+
].filter(Boolean);
|
|
2875
2931
|
}
|
|
2876
2932
|
/**
|
|
2877
2933
|
* The agent view's rows: families folded until opened, and an opened agent's own steps (its turns and tool calls, not its
|
|
@@ -2923,6 +2979,8 @@ function stepRuns(steps) {
|
|
|
2923
2979
|
hasChildren: false,
|
|
2924
2980
|
descendants: 0,
|
|
2925
2981
|
segments: [],
|
|
2982
|
+
...stepStats(run),
|
|
2983
|
+
tokens: sumTokens(run),
|
|
2926
2984
|
own: run.map((step) => ({
|
|
2927
2985
|
startMs: step.span.startMs,
|
|
2928
2986
|
endMs: step.span.endMs,
|
|
@@ -3114,58 +3172,110 @@ const KIND_LABEL = {
|
|
|
3114
3172
|
waiting: "Waiting",
|
|
3115
3173
|
handoff: "Handoff"
|
|
3116
3174
|
};
|
|
3117
|
-
const
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
children: formatCount(n)
|
|
3175
|
+
const count = (n, unit) => /* @__PURE__ */ jsxs("span", {
|
|
3176
|
+
title: `${n.toLocaleString()} ${unit}`,
|
|
3177
|
+
children: [
|
|
3178
|
+
formatCount(n),
|
|
3179
|
+
" ",
|
|
3180
|
+
unit
|
|
3181
|
+
]
|
|
3125
3182
|
});
|
|
3126
|
-
|
|
3183
|
+
const plural = (n, one, many = `${one}s`) => `${n.toLocaleString()} ${n === 1 ? one : many}`;
|
|
3184
|
+
/** Nearest-rank percentile of ascending values. */
|
|
3185
|
+
const percentile = (sorted, p) => sorted[Math.min(sorted.length - 1, Math.ceil(p * sorted.length) - 1)];
|
|
3186
|
+
const TOOLS_SHOWN = 4;
|
|
3187
|
+
/** The card's table: what the row recorded, every value with its unit. A value nobody recorded is left out, never printed as zero. */
|
|
3188
|
+
function metrics(row, clockMs, full) {
|
|
3189
|
+
const span = row.span;
|
|
3190
|
+
const out = [];
|
|
3191
|
+
if (span.detail) out.push([span.kind === "tool" ? "Provider" : "Model", span.detail]);
|
|
3192
|
+
const stop = spanRunning(span) ? Math.max(span.endMs, clockMs) : span.endMs;
|
|
3193
|
+
out.push(["Duration", Number.isFinite(stop - span.startMs) && stop >= span.startMs ? formatSpanDuration(stop - span.startMs) : "—"]);
|
|
3194
|
+
if (full) out.push(["Started", `+${formatSpanDuration(Math.max(0, span.startMs))}`]);
|
|
3195
|
+
const tokens = row.tokens;
|
|
3196
|
+
if (tokens) {
|
|
3197
|
+
const parts = [
|
|
3198
|
+
["prompt", "in"],
|
|
3199
|
+
["completion", "out"],
|
|
3200
|
+
["cached", "cached"]
|
|
3201
|
+
].flatMap(([key, unit]) => tokens[key] === void 0 ? [] : [count(tokens[key], unit)]);
|
|
3202
|
+
out.push(["Tokens", /* @__PURE__ */ jsx("span", {
|
|
3203
|
+
className: "fw-card-parts",
|
|
3204
|
+
children: parts.map((part, i) => /* @__PURE__ */ jsx("span", { children: part }, i))
|
|
3205
|
+
})]);
|
|
3206
|
+
}
|
|
3207
|
+
const latency = row.modelMs;
|
|
3208
|
+
if (latency?.length) out.push(["LLM latency", /* @__PURE__ */ jsxs("span", {
|
|
3209
|
+
className: "fw-card-parts",
|
|
3210
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [formatSpanDuration(percentile(latency, .5)), " median"] }), latency.length >= 10 && /* @__PURE__ */ jsxs("span", { children: [formatSpanDuration(percentile(latency, .95)), " p95"] })]
|
|
3211
|
+
})]);
|
|
3212
|
+
const cost = span.costUsd ?? row.rollupCostUsd;
|
|
3213
|
+
if (cost !== void 0) out.push(["Cost", rowCostText(row, cost)]);
|
|
3214
|
+
const counts = row.counts;
|
|
3215
|
+
if (counts?.model) out.push(["LLM calls", counts.model.toLocaleString()]);
|
|
3216
|
+
if (counts?.tool) {
|
|
3217
|
+
const tools = Object.entries(counts.tools).sort(([a, x], [b, y]) => y - x || a.localeCompare(b));
|
|
3218
|
+
const rest = tools.length - TOOLS_SHOWN;
|
|
3219
|
+
out.push(["Tool calls", /* @__PURE__ */ jsxs(Fragment, { children: [counts.tool.toLocaleString(), /* @__PURE__ */ jsxs("span", {
|
|
3220
|
+
className: "fw-card-tools",
|
|
3221
|
+
title: tools.map(([tool, n]) => `${tool} ${n}`).join(", "),
|
|
3222
|
+
children: [tools.slice(0, rest > 1 ? TOOLS_SHOWN : tools.length).map(([tool, n]) => /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", { children: tool }), /* @__PURE__ */ jsx("span", { children: n.toLocaleString() })] }, tool)), rest > 1 && /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsxs("span", { children: [rest, " more tools"] }), /* @__PURE__ */ jsx("span", { children: tools.slice(TOOLS_SHOWN).reduce((sum, [, n]) => sum + n, 0).toLocaleString() })] })]
|
|
3223
|
+
})] })]);
|
|
3224
|
+
}
|
|
3225
|
+
if (counts?.failed) out.push(["Failures", plural(counts.failed, "failed step")]);
|
|
3226
|
+
const restarts = span.marks?.filter((mark) => mark.kind === "restart").length ?? 0;
|
|
3227
|
+
if (counts?.retried || restarts) out.push(["Retries", [counts?.retried ? plural(counts.retried, "step retried", "steps retried") : "", restarts ? plural(restarts, "restart") : ""].filter(Boolean).join(" · ")]);
|
|
3228
|
+
if (full && row.hasChildren) out.push(["Steps", row.descendants.toLocaleString()]);
|
|
3229
|
+
for (const fact of span.facts ?? []) if (!out.some(([label]) => label.toLowerCase() === fact.label.toLowerCase())) out.push([fact.label, fact.value]);
|
|
3230
|
+
return out;
|
|
3231
|
+
}
|
|
3232
|
+
function Table({ rows }) {
|
|
3233
|
+
return /* @__PURE__ */ jsx("dl", {
|
|
3234
|
+
className: "fw-card-table",
|
|
3235
|
+
children: rows.map(([name, value]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: value })] }, name))
|
|
3236
|
+
});
|
|
3237
|
+
}
|
|
3238
|
+
/** The span card: what ran, for how long, on what tokens and at what recorded cost, then its input and output. */
|
|
3127
3239
|
function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderIcon, compact }) {
|
|
3128
3240
|
const span = row.span;
|
|
3129
3241
|
const status = spanRunning(span) ? "Running" : row.ok ? "Done" : "Failed";
|
|
3130
|
-
const tokens = span.kind === "model" || span.kind === "pipeline" || span.promptTokens !== void 0;
|
|
3131
3242
|
const owner = agent && agent.id !== row.id ? agent : void 0;
|
|
3132
|
-
const
|
|
3133
|
-
|
|
3134
|
-
|
|
3243
|
+
const frame = {
|
|
3244
|
+
id,
|
|
3245
|
+
role: pinned ? "dialog" : "tooltip",
|
|
3246
|
+
"aria-label": pinned ? `${span.name} details` : void 0,
|
|
3247
|
+
style: {
|
|
3248
|
+
left,
|
|
3249
|
+
top
|
|
3250
|
+
},
|
|
3251
|
+
onKeyDown: (event) => {
|
|
3252
|
+
if (event.key === "Escape") {
|
|
3253
|
+
event.stopPropagation();
|
|
3254
|
+
onClose();
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3135
3257
|
};
|
|
3136
|
-
const
|
|
3258
|
+
const close = pinned && /* @__PURE__ */ jsx("button", {
|
|
3259
|
+
type: "button",
|
|
3260
|
+
className: "fw-icon-button fw-card-close",
|
|
3261
|
+
"aria-label": "Close details",
|
|
3262
|
+
onClick: onClose,
|
|
3263
|
+
autoFocus: true,
|
|
3264
|
+
children: /* @__PURE__ */ jsx(CloseGlyph, {})
|
|
3265
|
+
});
|
|
3137
3266
|
if (compact) {
|
|
3138
3267
|
const who = owner ?? row;
|
|
3139
|
-
const
|
|
3140
|
-
const outcome = subject.outcome;
|
|
3141
|
-
const tokenCount = subject.promptTokens !== void 0 || subject.completionTokens !== void 0 ? (subject.promptTokens ?? 0) + (subject.completionTokens ?? 0) : void 0;
|
|
3142
|
-
const cost = subject.costUsd ?? who.rollupCostUsd;
|
|
3143
|
-
const cells = [[subject.kind === "model" ? "Thinking" : KIND_LABEL[subject.kind], lasted(subject)]];
|
|
3144
|
-
for (const fact of (subject.facts ?? []).slice(0, 3)) cells.push([fact.label, fact.value]);
|
|
3145
|
-
cells.push(["Started", `+${formatSpanDuration(Math.max(0, subject.startMs))}`]);
|
|
3146
|
-
if (tokenCount !== void 0) cells.push(["Tokens", count(tokenCount)]);
|
|
3147
|
-
if (cost !== void 0) cells.push(["Cost", rowCostText(who, cost)]);
|
|
3148
|
-
cells.splice(6);
|
|
3268
|
+
const outcome = who.span.outcome;
|
|
3149
3269
|
const kind = span.kind === "model" ? "Thinking" : KIND_LABEL[span.kind];
|
|
3150
|
-
const
|
|
3270
|
+
const named = span.name && span.name.toLowerCase() !== kind.toLowerCase() && span.name.toLowerCase() !== span.kind ? ` ${span.name}` : "";
|
|
3271
|
+
const stop = spanRunning(span) ? Math.max(span.endMs, clockMs) : span.endMs;
|
|
3272
|
+
const step = owner && span.endMs > span.startMs ? `${kind}${named} · ${formatSpanDuration(stop - span.startMs)}${row.ok ? "" : " · failed"}` : void 0;
|
|
3151
3273
|
const sentence = !owner && span.output && span.output !== who.name ? span.output : void 0;
|
|
3152
3274
|
return /* @__PURE__ */ jsxs("div", {
|
|
3153
|
-
|
|
3154
|
-
role: pinned ? "dialog" : "tooltip",
|
|
3155
|
-
"aria-label": pinned ? `${span.name} details` : void 0,
|
|
3275
|
+
...frame,
|
|
3156
3276
|
className: "fw-card fw-card-compact",
|
|
3157
3277
|
"data-tone": toneKey(who.tone),
|
|
3158
3278
|
"data-outcome": outcome?.tone,
|
|
3159
|
-
style: {
|
|
3160
|
-
left,
|
|
3161
|
-
top
|
|
3162
|
-
},
|
|
3163
|
-
onKeyDown: (event) => {
|
|
3164
|
-
if (event.key === "Escape") {
|
|
3165
|
-
event.stopPropagation();
|
|
3166
|
-
onClose();
|
|
3167
|
-
}
|
|
3168
|
-
},
|
|
3169
3279
|
children: [
|
|
3170
3280
|
/* @__PURE__ */ jsxs("div", {
|
|
3171
3281
|
className: "fw-card-agent",
|
|
@@ -3187,28 +3297,10 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
3187
3297
|
className: `fw-pill fw-pill-${status.toLowerCase()}`,
|
|
3188
3298
|
children: status
|
|
3189
3299
|
}),
|
|
3190
|
-
|
|
3191
|
-
type: "button",
|
|
3192
|
-
className: "fw-icon-button",
|
|
3193
|
-
style: {
|
|
3194
|
-
width: 20,
|
|
3195
|
-
height: 20
|
|
3196
|
-
},
|
|
3197
|
-
"aria-label": "Close details",
|
|
3198
|
-
onClick: onClose,
|
|
3199
|
-
autoFocus: true,
|
|
3200
|
-
children: /* @__PURE__ */ jsx(CloseGlyph, {})
|
|
3201
|
-
})
|
|
3300
|
+
close
|
|
3202
3301
|
]
|
|
3203
3302
|
}),
|
|
3204
|
-
|
|
3205
|
-
className: "fw-card-detail",
|
|
3206
|
-
children: who.span.detail
|
|
3207
|
-
}),
|
|
3208
|
-
/* @__PURE__ */ jsx("dl", {
|
|
3209
|
-
className: "fw-card-cells",
|
|
3210
|
-
children: cells.map(([name, value]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: value })] }, name))
|
|
3211
|
-
}),
|
|
3303
|
+
/* @__PURE__ */ jsx(Table, { rows: metrics(who, clockMs, false) }),
|
|
3212
3304
|
step && /* @__PURE__ */ jsx("p", {
|
|
3213
3305
|
className: "fw-card-step",
|
|
3214
3306
|
children: step
|
|
@@ -3221,35 +3313,16 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
3221
3313
|
});
|
|
3222
3314
|
}
|
|
3223
3315
|
return /* @__PURE__ */ jsxs("div", {
|
|
3224
|
-
|
|
3225
|
-
role: pinned ? "dialog" : "tooltip",
|
|
3226
|
-
"aria-label": pinned ? `${span.name} details` : void 0,
|
|
3316
|
+
...frame,
|
|
3227
3317
|
className: "fw-card",
|
|
3228
3318
|
"data-tone": toneKey(row.tone),
|
|
3229
|
-
style: {
|
|
3230
|
-
left,
|
|
3231
|
-
top
|
|
3232
|
-
},
|
|
3233
|
-
onKeyDown: (event) => {
|
|
3234
|
-
if (event.key === "Escape") {
|
|
3235
|
-
event.stopPropagation();
|
|
3236
|
-
onClose();
|
|
3237
|
-
}
|
|
3238
|
-
},
|
|
3239
3319
|
children: [
|
|
3240
3320
|
owner && /* @__PURE__ */ jsxs("div", {
|
|
3241
3321
|
className: "fw-card-agent",
|
|
3242
|
-
children: [
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
}),
|
|
3247
|
-
owner.name,
|
|
3248
|
-
owner.span.detail && /* @__PURE__ */ jsxs("span", {
|
|
3249
|
-
className: "fw-muted",
|
|
3250
|
-
children: [" ", owner.span.detail]
|
|
3251
|
-
})
|
|
3252
|
-
]
|
|
3322
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
3323
|
+
className: "fw-agent-dot",
|
|
3324
|
+
"aria-hidden": true
|
|
3325
|
+
}), owner.name]
|
|
3253
3326
|
}),
|
|
3254
3327
|
/* @__PURE__ */ jsxs("div", {
|
|
3255
3328
|
className: "fw-card-head",
|
|
@@ -3262,42 +3335,18 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
3262
3335
|
name: span.name
|
|
3263
3336
|
})
|
|
3264
3337
|
}),
|
|
3265
|
-
/* @__PURE__ */
|
|
3266
|
-
className: "fw-card-
|
|
3267
|
-
children:
|
|
3268
|
-
className: "fw-card-name",
|
|
3269
|
-
children: keepWords(span.name)
|
|
3270
|
-
}), span.detail && /* @__PURE__ */ jsx("div", {
|
|
3271
|
-
className: "fw-muted",
|
|
3272
|
-
children: span.detail
|
|
3273
|
-
})]
|
|
3338
|
+
/* @__PURE__ */ jsx("div", {
|
|
3339
|
+
className: "fw-card-name",
|
|
3340
|
+
children: keepWords(span.name)
|
|
3274
3341
|
}),
|
|
3275
3342
|
/* @__PURE__ */ jsx("span", {
|
|
3276
3343
|
className: `fw-pill fw-pill-${status.toLowerCase()}`,
|
|
3277
3344
|
children: status
|
|
3278
3345
|
}),
|
|
3279
|
-
|
|
3280
|
-
type: "button",
|
|
3281
|
-
className: "fw-icon-button",
|
|
3282
|
-
"aria-label": "Close details",
|
|
3283
|
-
onClick: onClose,
|
|
3284
|
-
autoFocus: true,
|
|
3285
|
-
children: /* @__PURE__ */ jsx(CloseGlyph, {})
|
|
3286
|
-
})
|
|
3287
|
-
]
|
|
3288
|
-
}),
|
|
3289
|
-
/* @__PURE__ */ jsxs("dl", {
|
|
3290
|
-
className: "fw-card-stats",
|
|
3291
|
-
children: [
|
|
3292
|
-
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Duration" }), /* @__PURE__ */ jsx("dd", { children: duration })] }),
|
|
3293
|
-
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Started" }), /* @__PURE__ */ jsxs("dd", { children: ["+", formatSpanDuration(Math.max(0, span.startMs))] })] }),
|
|
3294
|
-
span.costUsd !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Cost" }), /* @__PURE__ */ jsx("dd", { children: rowCostText(row, span.costUsd) })] }),
|
|
3295
|
-
tokens && span.promptTokens !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Tokens in" }), /* @__PURE__ */ jsx("dd", { children: count(span.promptTokens) })] }),
|
|
3296
|
-
tokens && span.completionTokens !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Tokens out" }), /* @__PURE__ */ jsx("dd", { children: count(span.completionTokens) })] }),
|
|
3297
|
-
(span.facts ?? []).slice(0, 3).map((fact) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: fact.label }), /* @__PURE__ */ jsx("dd", { children: fact.value })] }, fact.label)),
|
|
3298
|
-
row.hasChildren && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Steps" }), /* @__PURE__ */ jsx("dd", { children: row.descendants })] })
|
|
3346
|
+
close
|
|
3299
3347
|
]
|
|
3300
3348
|
}),
|
|
3349
|
+
/* @__PURE__ */ jsx(Table, { rows: metrics(row, clockMs, true) }),
|
|
3301
3350
|
span.input && /* @__PURE__ */ jsxs("div", {
|
|
3302
3351
|
className: "fw-card-text",
|
|
3303
3352
|
children: [/* @__PURE__ */ jsx("div", {
|
|
@@ -3323,7 +3372,7 @@ const at = (startMs, endMs) => ({
|
|
|
3323
3372
|
"--s": startMs,
|
|
3324
3373
|
"--e": Number.isFinite(endMs) ? endMs : OPEN_END
|
|
3325
3374
|
});
|
|
3326
|
-
const endLabelWidth = (label, slowest, costliest) => label.length * 7 + 8 + (slowest ?
|
|
3375
|
+
const endLabelWidth = (label, slowest, costliest) => label.length * 7 + 8 + (slowest ? 80 : 0) + (costliest ? 90 : 0);
|
|
3327
3376
|
function Segment({ mark, events, agentView, ending }) {
|
|
3328
3377
|
const glyph = mark.kind === "failed" ? agentView && !ending ? null : /* @__PURE__ */ jsx(FailGlyph, {}) : mark.retry ? /* @__PURE__ */ jsx(RetryGlyph, {}) : null;
|
|
3329
3378
|
const named = agentView && mark.kind === "tool" && (mark.count === 1 ? mark.name : mark.tool && `${mark.tool} ×${mark.count}`);
|
|
@@ -3332,10 +3381,10 @@ function Segment({ mark, events, agentView, ending }) {
|
|
|
3332
3381
|
"data-tone": toneKey(mark.tone),
|
|
3333
3382
|
style: at(mark.startMs, mark.endMs),
|
|
3334
3383
|
title: mark.name ? `${mark.name}${mark.count > 1 ? ` +${mark.count - 1} more` : ""}` : void 0,
|
|
3335
|
-
onPointerEnter: agentView ? (event) => events.onInspect(mark.id, event.currentTarget) : void 0,
|
|
3384
|
+
onPointerEnter: agentView ? (event) => events.onInspect(mark.id, event.currentTarget, event.clientX) : void 0,
|
|
3336
3385
|
onClick: agentView ? (event) => {
|
|
3337
3386
|
event.stopPropagation();
|
|
3338
|
-
events.onPin(mark.id, event.currentTarget);
|
|
3387
|
+
events.onPin(mark.id, event.currentTarget, event.clientX);
|
|
3339
3388
|
} : void 0,
|
|
3340
3389
|
children: glyph ? /* @__PURE__ */ jsx("span", {
|
|
3341
3390
|
className: "fw-glyph",
|
|
@@ -3369,8 +3418,8 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3369
3418
|
const kind = !row.ok ? "failed" : row.kind;
|
|
3370
3419
|
const marks = isAgent || row.hasChildren || row.id.startsWith("run:") ? folded && !agentView ? row.segments : row.own : [];
|
|
3371
3420
|
const label = endLabel(row, clockMs);
|
|
3372
|
-
const summary =
|
|
3373
|
-
const inspect = (event) => props.onInspect(row.id, event.currentTarget);
|
|
3421
|
+
const summary = [...span.detail && span.detail !== props.sharedDetail ? [span.detail] : [], ...agentSummary(row)];
|
|
3422
|
+
const inspect = (event) => props.onInspect(row.id, event.currentTarget, event.clientX);
|
|
3374
3423
|
const family = agentView && row.id.startsWith("family:");
|
|
3375
3424
|
const step = agentView && row.stepOf !== void 0;
|
|
3376
3425
|
const opens = family || agentView && isAgent && row.own.length > 0;
|
|
@@ -3392,7 +3441,8 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3392
3441
|
className: `fw-row${active ? " fw-active" : ""}${sticky ? " fw-sticky-row" : ""}${isAgent ? " fw-agent-row" : ""}${step ? " fw-step" : ""}${motion?.enter !== void 0 ? " fw-enter" : ""}${motion?.settle ? " fw-settle" : ""}`,
|
|
3393
3442
|
style: {
|
|
3394
3443
|
"--row-y": `${top}px`,
|
|
3395
|
-
"--fw-enter-delay": `${motion?.enter ?? 0}ms
|
|
3444
|
+
"--fw-enter-delay": `${motion?.enter ?? 0}ms`,
|
|
3445
|
+
"--fw-end-w": `${endLabelWidth(label, slowest, costliest)}px`
|
|
3396
3446
|
},
|
|
3397
3447
|
onFocus: (event) => {
|
|
3398
3448
|
if (event.target === event.currentTarget) props.onActivate(row);
|
|
@@ -3404,7 +3454,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3404
3454
|
return;
|
|
3405
3455
|
}
|
|
3406
3456
|
const bar = agentView ? event.currentTarget.querySelector(".fw-bar") : null;
|
|
3407
|
-
if (bar) props.onPin(row.id, bar);
|
|
3457
|
+
if (bar) props.onPin(row.id, bar, event.target instanceof Element && event.target.closest(".fw-lane") ? event.clientX : void 0);
|
|
3408
3458
|
},
|
|
3409
3459
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
3410
3460
|
role: "gridcell",
|
|
@@ -3491,11 +3541,11 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3491
3541
|
className: "fw-subtitle-tail",
|
|
3492
3542
|
children: [/* @__PURE__ */ jsx("span", {
|
|
3493
3543
|
className: "fw-subtitle-text",
|
|
3494
|
-
children:
|
|
3495
|
-
}), summary
|
|
3544
|
+
children: summary[0]
|
|
3545
|
+
}), summary.slice(1).map((part) => /* @__PURE__ */ jsxs("span", {
|
|
3496
3546
|
className: "fw-subtitle-extra",
|
|
3497
|
-
children: ["· ",
|
|
3498
|
-
})]
|
|
3547
|
+
children: ["· ", part]
|
|
3548
|
+
}, part))]
|
|
3499
3549
|
})]
|
|
3500
3550
|
})]
|
|
3501
3551
|
}),
|
|
@@ -3535,7 +3585,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3535
3585
|
onClick: (event) => {
|
|
3536
3586
|
event.stopPropagation();
|
|
3537
3587
|
if (family) props.onToggle(row);
|
|
3538
|
-
else props.onPin(row.id, event.currentTarget);
|
|
3588
|
+
else props.onPin(row.id, event.currentTarget, event.clientX);
|
|
3539
3589
|
},
|
|
3540
3590
|
children: [!isAgent && !row.ok && /* @__PURE__ */ jsx("span", {
|
|
3541
3591
|
className: "fw-glyph",
|
|
@@ -3553,10 +3603,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3553
3603
|
}, `${mark.id}-${i}`)),
|
|
3554
3604
|
/* @__PURE__ */ jsxs("span", {
|
|
3555
3605
|
className: "fw-end",
|
|
3556
|
-
style:
|
|
3557
|
-
...at(span.startMs, running ? OPEN_END : span.endMs),
|
|
3558
|
-
"--fw-end-w": `${endLabelWidth(label, slowest, costliest)}px`
|
|
3559
|
-
},
|
|
3606
|
+
style: at(span.startMs, running ? OPEN_END : span.endMs),
|
|
3560
3607
|
children: [
|
|
3561
3608
|
/* @__PURE__ */ jsx("span", {
|
|
3562
3609
|
className: "fw-duration",
|
|
@@ -3820,22 +3867,6 @@ function FlowBody(props) {
|
|
|
3820
3867
|
trackPx,
|
|
3821
3868
|
view
|
|
3822
3869
|
]);
|
|
3823
|
-
const pauseLabels = useMemo(() => {
|
|
3824
|
-
const shown = /* @__PURE__ */ new Set();
|
|
3825
|
-
let freeFrom = -Infinity;
|
|
3826
|
-
for (const pause of [...props.pauses ?? []].sort((a, b) => a.startMs - b.startMs)) {
|
|
3827
|
-
const width = duration(pause.endMs - pause.startMs).length * 7 + 24;
|
|
3828
|
-
const centre = ((pause.startMs + pause.endMs) / 2 - win.startMs) / msPerPx;
|
|
3829
|
-
if (centre - width / 2 < freeFrom) continue;
|
|
3830
|
-
shown.add(pause);
|
|
3831
|
-
freeFrom = centre + width / 2 + 8;
|
|
3832
|
-
}
|
|
3833
|
-
return shown;
|
|
3834
|
-
}, [
|
|
3835
|
-
props.pauses,
|
|
3836
|
-
win.startMs,
|
|
3837
|
-
msPerPx
|
|
3838
|
-
]);
|
|
3839
3870
|
useEffect(() => {
|
|
3840
3871
|
const element = scroller.current;
|
|
3841
3872
|
if (!element) return;
|
|
@@ -4017,18 +4048,21 @@ function FlowBody(props) {
|
|
|
4017
4048
|
props.onScrub(marker.at);
|
|
4018
4049
|
}
|
|
4019
4050
|
}, `e-${marker.at}-${marker.label}`)),
|
|
4020
|
-
props.pauses?.
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4051
|
+
props.pauses?.map((pause) => {
|
|
4052
|
+
const said = `${pause.label} ${duration(pause.endMs - pause.startMs)}${pause.detail ? `: ${pause.detail}` : ""}`;
|
|
4053
|
+
return /* @__PURE__ */ jsx("span", {
|
|
4054
|
+
className: "fw-pause-mark",
|
|
4055
|
+
style: {
|
|
4056
|
+
"--s": pause.startMs,
|
|
4057
|
+
"--e": pause.endMs
|
|
4058
|
+
},
|
|
4059
|
+
title: said,
|
|
4060
|
+
"aria-label": said,
|
|
4061
|
+
role: "img",
|
|
4062
|
+
tabIndex: 0,
|
|
4063
|
+
onPointerDown: (event) => event.stopPropagation()
|
|
4064
|
+
}, `pm-${pause.startMs}`);
|
|
4065
|
+
}),
|
|
4032
4066
|
jumped && /* @__PURE__ */ jsx("span", {
|
|
4033
4067
|
className: "fw-event-label",
|
|
4034
4068
|
"data-tone": jumped.tone ?? "neutral",
|
|
@@ -4077,14 +4111,24 @@ function FlowBody(props) {
|
|
|
4077
4111
|
/* @__PURE__ */ jsxs("div", {
|
|
4078
4112
|
className: "fw-gridlayer",
|
|
4079
4113
|
"aria-hidden": true,
|
|
4080
|
-
children: [
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4114
|
+
children: [
|
|
4115
|
+
props.pauses?.map((pause) => /* @__PURE__ */ jsx("span", {
|
|
4116
|
+
className: "fw-pause",
|
|
4117
|
+
style: {
|
|
4118
|
+
"--s": pause.startMs,
|
|
4119
|
+
"--e": pause.endMs
|
|
4120
|
+
}
|
|
4121
|
+
}, `p-${pause.startMs}`)),
|
|
4122
|
+
ticks.map((tick) => /* @__PURE__ */ jsx("span", {
|
|
4123
|
+
className: "fw-gridline",
|
|
4124
|
+
style: { "--s": tick }
|
|
4125
|
+
}, tick)),
|
|
4126
|
+
props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
|
|
4127
|
+
className: "fw-marker-line",
|
|
4128
|
+
"data-tone": marker.tone ?? "neutral",
|
|
4129
|
+
style: { "--s": marker.at }
|
|
4130
|
+
}, `l-${marker.at}-${marker.label}`))
|
|
4131
|
+
]
|
|
4088
4132
|
}),
|
|
4089
4133
|
/* @__PURE__ */ jsxs("div", {
|
|
4090
4134
|
ref: scroller,
|
|
@@ -4129,16 +4173,9 @@ function FlowBody(props) {
|
|
|
4129
4173
|
rowHeight,
|
|
4130
4174
|
render: row
|
|
4131
4175
|
}),
|
|
4132
|
-
props.markers?.length
|
|
4176
|
+
props.markers?.length ? /* @__PURE__ */ jsx("div", {
|
|
4133
4177
|
className: "fw-marker-layer",
|
|
4134
|
-
children:
|
|
4135
|
-
className: "fw-pause",
|
|
4136
|
-
style: {
|
|
4137
|
-
"--s": pause.startMs,
|
|
4138
|
-
"--e": pause.endMs
|
|
4139
|
-
},
|
|
4140
|
-
title: `${pause.label}, ${duration(pause.endMs - pause.startMs)}`
|
|
4141
|
-
}, `p-${pause.startMs}`)), props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
|
|
4178
|
+
children: props.markers.map((marker) => /* @__PURE__ */ jsx("span", {
|
|
4142
4179
|
className: "fw-marker",
|
|
4143
4180
|
"data-tone": marker.tone ?? "neutral",
|
|
4144
4181
|
style: { "--s": marker.at },
|
|
@@ -4147,7 +4184,7 @@ function FlowBody(props) {
|
|
|
4147
4184
|
className: `fw-marker-label${markerLabels.get(marker) === "left" ? " fw-marker-label-left" : ""}`,
|
|
4148
4185
|
children: marker.label
|
|
4149
4186
|
})
|
|
4150
|
-
}, `${marker.at}-${marker.label}`))
|
|
4187
|
+
}, `${marker.at}-${marker.label}`))
|
|
4151
4188
|
}) : null,
|
|
4152
4189
|
showCursor && props.cursorLine !== false && /* @__PURE__ */ jsx("div", {
|
|
4153
4190
|
className: "fw-cursor-layer",
|
|
@@ -4443,6 +4480,7 @@ function useHostable(value, onChange, initial) {
|
|
|
4443
4480
|
report.current?.(resolved);
|
|
4444
4481
|
}, [controlled])];
|
|
4445
4482
|
}
|
|
4483
|
+
const CARD_WIDTH = 320;
|
|
4446
4484
|
/** The summary strip: as of the playhead while scrubbing, the producer's totals otherwise. */
|
|
4447
4485
|
function summaryCells(trace, at, durationMs) {
|
|
4448
4486
|
const cost = costSummary(at ? at.costUsd : trace.costUsd, trace);
|
|
@@ -4620,7 +4658,8 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4620
4658
|
const pipelines = rows.filter((row) => row.kind === "pipeline");
|
|
4621
4659
|
return pipelines.length ? pipelines : rows.filter((row) => !row.parentId);
|
|
4622
4660
|
}, [rows]);
|
|
4623
|
-
const
|
|
4661
|
+
const details = new Set(agents.flatMap((row) => row.span.detail ? [row.span.detail] : []));
|
|
4662
|
+
const sharedDetail = agents.length > 1 && details.size === 1 ? [...details][0] : void 0;
|
|
4624
4663
|
const [flippedFamilies, setFlippedFamilies] = useState(/* @__PURE__ */ new Set());
|
|
4625
4664
|
const openFamilies = useMemo(() => defaultOpenFamilies(agents, UNFOLD_ROWS, flippedFamilies), [agents, flippedFamilies]);
|
|
4626
4665
|
const [openSteps, setOpenSteps] = useState(/* @__PURE__ */ new Set());
|
|
@@ -4735,8 +4774,8 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4735
4774
|
}, limit));
|
|
4736
4775
|
const latest = useRef({
|
|
4737
4776
|
toggle: (_row) => {},
|
|
4738
|
-
pin: (_target, _element) => {},
|
|
4739
|
-
inspect: (_target, _element) => {}
|
|
4777
|
+
pin: (_target, _element, _x) => {},
|
|
4778
|
+
inspect: (_target, _element, _x) => {}
|
|
4740
4779
|
});
|
|
4741
4780
|
const toggleNow = (row) => {
|
|
4742
4781
|
const flip = (open) => new Set(open.has(row.id) ? [...open].filter((id) => id !== row.id) : [...open, row.id]);
|
|
@@ -4760,25 +4799,43 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4760
4799
|
};
|
|
4761
4800
|
latest.current.toggle = toggleNow;
|
|
4762
4801
|
const toggle = useCallback((row) => latest.current.toggle(row), []);
|
|
4763
|
-
const place = (target, element, pinned) => {
|
|
4802
|
+
const place = (target, element, pinned, x) => {
|
|
4764
4803
|
const host = root.current?.getBoundingClientRect();
|
|
4765
4804
|
if (!host) return;
|
|
4766
4805
|
const rect = element.getBoundingClientRect();
|
|
4767
|
-
const
|
|
4768
|
-
const left = Math.max(8, Math.min(rect.left - host.left, host.width -
|
|
4806
|
+
const width = Math.min(CARD_WIDTH, host.width - 16);
|
|
4807
|
+
const left = Math.max(8, Math.min((x ?? rect.left + 24) - host.left - 24, host.width - width - 8));
|
|
4769
4808
|
clearTimeout(hideTimer.current);
|
|
4770
4809
|
setCard({
|
|
4771
4810
|
target,
|
|
4772
4811
|
pinned,
|
|
4773
4812
|
left,
|
|
4774
|
-
|
|
4775
|
-
|
|
4813
|
+
anchorTop: rect.top - host.top,
|
|
4814
|
+
anchorBottom: rect.bottom - host.top,
|
|
4815
|
+
above: view === "agents"
|
|
4776
4816
|
});
|
|
4777
4817
|
};
|
|
4778
|
-
|
|
4779
|
-
if (!card
|
|
4818
|
+
useLayoutEffect(() => {
|
|
4819
|
+
if (!card || card.fitted || !root.current) return;
|
|
4820
|
+
const box = document.getElementById(cardId)?.getBoundingClientRect();
|
|
4821
|
+
if (!box) return;
|
|
4822
|
+
const hostTop = root.current.getBoundingClientRect().top;
|
|
4823
|
+
const room = {
|
|
4824
|
+
above: hostTop + card.anchorTop - 16,
|
|
4825
|
+
below: globalThis.innerHeight - (hostTop + card.anchorBottom) - 16
|
|
4826
|
+
};
|
|
4827
|
+
const preferred = card.above ? room.above : room.below;
|
|
4828
|
+
const other = card.above ? room.below : room.above;
|
|
4829
|
+
setCard({
|
|
4830
|
+
...card,
|
|
4831
|
+
fitted: true,
|
|
4832
|
+
above: box.height <= preferred || box.height > other && preferred >= other ? card.above : !card.above
|
|
4833
|
+
});
|
|
4834
|
+
}, [card, cardId]);
|
|
4835
|
+
latest.current.inspect = (target, element, x) => {
|
|
4836
|
+
if (!card?.pinned) place(target, element, false, x);
|
|
4780
4837
|
};
|
|
4781
|
-
const inspect = useCallback((target, element) => latest.current.inspect(target, element), []);
|
|
4838
|
+
const inspect = useCallback((target, element, x) => latest.current.inspect(target, element, x), []);
|
|
4782
4839
|
const leave = useCallback(() => {
|
|
4783
4840
|
clearTimeout(hideTimer.current);
|
|
4784
4841
|
hideTimer.current = setTimeout(() => setCard((previous) => previous?.pinned ? previous : null), 140);
|
|
@@ -4791,19 +4848,19 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4791
4848
|
document.addEventListener("pointermove", move);
|
|
4792
4849
|
return () => document.removeEventListener("pointermove", move);
|
|
4793
4850
|
}, [card, leave]);
|
|
4794
|
-
latest.current.pin = (target, element) => {
|
|
4851
|
+
latest.current.pin = (target, element, x) => {
|
|
4795
4852
|
if (card?.pinned && card.target === target) {
|
|
4796
4853
|
closeCard();
|
|
4797
4854
|
return;
|
|
4798
4855
|
}
|
|
4799
|
-
place(target, element, true);
|
|
4856
|
+
place(target, element, true, x);
|
|
4800
4857
|
const row = byId.get(target);
|
|
4801
4858
|
if (row) {
|
|
4802
4859
|
onSelect?.(row.span);
|
|
4803
4860
|
zoomTo(row);
|
|
4804
4861
|
}
|
|
4805
4862
|
};
|
|
4806
|
-
const pin = useCallback((target, element) => latest.current.pin(target, element), []);
|
|
4863
|
+
const pin = useCallback((target, element, x) => latest.current.pin(target, element, x), []);
|
|
4807
4864
|
const selection = selectedId ?? (card?.pinned ? card.target : void 0);
|
|
4808
4865
|
const closeCard = () => {
|
|
4809
4866
|
const target = card?.target;
|
|
@@ -5056,7 +5113,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
5056
5113
|
agent: cardAgent,
|
|
5057
5114
|
clockMs,
|
|
5058
5115
|
left: card.left,
|
|
5059
|
-
top: card.
|
|
5116
|
+
top: card.above ? card.anchorTop - 8 : card.anchorBottom + 8,
|
|
5060
5117
|
pinned: card.pinned,
|
|
5061
5118
|
onClose: closeCard,
|
|
5062
5119
|
renderIcon,
|