MCPcopy Create free account
hub / github.com/microsoft/data-formulator / computeSplitExtraLeaves

Function computeSplitExtraLeaves

src/views/DataThread.tsx:2505–2582  ·  view source on GitHub ↗

* For each long thread, identify intermediate tables to "promote" as extra * leaves so the thread renders as multiple stacked segments. * * Item-count strategy (no pixel estimates): * * 1. Count "items" per thread: each trigger contributes 1 + #effective * interaction entries + #charts

(
    leafTables: DictTable[],
    allTables: DictTable[],
    chartElements: { tableId: string }[],
    fittableColumns: number,
)

Source from the content-addressed store, hash-verified

2503 * instruction with the promoted table shown as a dimmed ghost.
2504 */
2505function computeSplitExtraLeaves(
2506 leafTables: DictTable[],
2507 allTables: DictTable[],
2508 chartElements: { tableId: string }[],
2509 fittableColumns: number,
2510): DictTable[] {
2511 if (fittableColumns <= 1) return [];
2512 const tableById = new Map(allTables.map(t => [t.id, t]));
2513
2514 // Per-trigger item count = 1 (table) + interaction entries + charts.
2515 const itemsForTrigger = (resultTableId: string, interaction: InteractionEntry[] | undefined): number => {
2516 const charts = chartElements.filter(ce => ce.tableId === resultTableId).length;
2517 return 1 + effectiveEntryCount(interaction) + charts;
2518 };
2519
2520 // Compute per-thread totals up front so we can pick the budget.
2521 const triggersByLeaf: Trigger[][] = [];
2522 const threadItems: number[] = [];
2523 for (const lt of leafTables) {
2524 const triggers = getTriggers(lt, allTables);
2525 triggersByLeaf.push(triggers);
2526 let items = 0;
2527 for (const tp of triggers) items += itemsForTrigger(tp.resultTableId, tp.interaction);
2528 // Leaf trigger contributes its own row count + leaf table + leaf charts.
2529 items += itemsForTrigger(lt.id, lt.derive?.trigger?.interaction);
2530 threadItems.push(items);
2531 }
2532 const totalItems = threadItems.reduce((s, v) => s + v, 0);
2533 if (totalItems === 0) return [];
2534
2535 const budget = totalItems / fittableColumns;
2536
2537 const extras: DictTable[] = [];
2538 for (let li = 0; li < leafTables.length; li++) {
2539 const lt = leafTables[li];
2540 if (!lt.derive) continue;
2541 const triggers = triggersByLeaf[li];
2542 if (triggers.length < 3) continue;
2543
2544 const K = Math.max(1, Math.round(threadItems[li] / budget));
2545 if (K <= 1) continue;
2546
2547 // Per-trigger items, with the leaf weight folded into the LAST entry
2548 // (the leaf step renders inside the trailing segment but isn't part
2549 // of the `triggers` array).
2550 const triggerItems = triggers.map(tp => itemsForTrigger(tp.resultTableId, tp.interaction));
2551 triggerItems[triggerItems.length - 1] += itemsForTrigger(lt.id, lt.derive.trigger?.interaction);
2552
2553 // Stability strategy: prefer greedy cuts (which only shift when
2554 // earlier content changes) over balanced ones (which redistribute
2555 // on every new trigger). Only re-balance when the trailing segment
2556 // grows past 1.5× the previous segment — i.e. greedy has produced
2557 // a noticeably lopsided layout that warrants a reshuffle.
2558 let cuts = greedyPartitionCuts(triggerItems, K, budget);
2559 if (cuts.length === 0) {
2560 // Greedy couldn't find K segments at this budget — fall back to
2561 // balanced (which uses binary search and always finds a valid
2562 // partition when one exists).

Callers 1

DataThreadFunction · 0.85

Calls 6

getTriggersFunction · 0.90
itemsForTriggerFunction · 0.85
greedyPartitionCutsFunction · 0.85
balancedPartitionCutsFunction · 0.85
segmentSumsFunction · 0.85
getMethod · 0.80

Tested by

no test coverage detected