@abreen/tada 1.7.0 → 1.7.1

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.
@@ -7,10 +7,10 @@ import type {
7
7
  } from '../types';
8
8
 
9
9
  /** Maximum number of array cells to display. */
10
- const ARRAY_MAX_CELLS = 12;
10
+ export const ARRAY_MAX_CELLS = 12;
11
11
 
12
12
  /** Default font size in pixels. */
13
- const DEFAULT_FONT_SIZE = 15;
13
+ export const DEFAULT_FONT_SIZE = 15;
14
14
 
15
15
  /** Gap between objects in the layout. */
16
16
  const OBJ_GAP = 24;
@@ -286,6 +286,46 @@ export function getObjectSize(
286
286
  return { width, height, valueX };
287
287
  }
288
288
 
289
+ /** Size info returned by getObjectSize. */
290
+ type ObjSize = {
291
+ width: number;
292
+ height: number;
293
+ valueX?: number;
294
+ cellWidth?: number;
295
+ };
296
+
297
+ /** Chain of non-tree objects laid out horizontally to the right of a tree node. */
298
+ interface RefChain {
299
+ ids: string[];
300
+ totalWidth: number;
301
+ maxHeight: number;
302
+ }
303
+
304
+ /** Construct a TraceObjectLayout, copying optional size and child field info. */
305
+ function makeLayout(
306
+ x: number,
307
+ y: number,
308
+ size: ObjSize,
309
+ childFields?: Set<string>,
310
+ ): TraceObjectLayout {
311
+ const ol: TraceObjectLayout = {
312
+ x,
313
+ y,
314
+ width: size.width,
315
+ height: size.height,
316
+ };
317
+ if (size.valueX !== undefined) {
318
+ ol.valueX = size.valueX;
319
+ }
320
+ if (size.cellWidth !== undefined) {
321
+ ol.cellWidth = size.cellWidth;
322
+ }
323
+ if (childFields && childFields.size > 0) {
324
+ ol.childFields = Array.from(childFields);
325
+ }
326
+ return ol;
327
+ }
328
+
289
329
  /**
290
330
  * The main entry point. Scans steps, builds supergraph, finds root objects,
291
331
  * builds a tree for each root, runs d3-flextree, translates positions to
@@ -304,9 +344,72 @@ export function computeLayout(
304
344
 
305
345
  const { graph, fieldToChild } = buildSupergraph(objects, ignoreFields);
306
346
 
307
- // Determine child fields for each object based on the supergraph.
308
- // A field is a "child field" if it ever pointed to a structural child.
309
- // First pass: find which field names are structural per type.
347
+ // Phase 1: classify types and compute structural metadata
348
+ const { chainTypes, objectChildFields, structuralChildren, roots } =
349
+ classifyTypes(objects, fieldToChild);
350
+
351
+ // Compute sizes for all objects (now with child field info)
352
+ const sizes = new Map<string, ObjSize>();
353
+ for (const [id, info] of objects) {
354
+ const cFields = objectChildFields.get(id) ?? new Set();
355
+ sizes.set(id, getObjectSize(info, fontSize, cFields));
356
+ }
357
+
358
+ // Phase 2: identify tree nodes and compute ref chains
359
+ const { refChains } = buildRefChains(
360
+ roots,
361
+ objects,
362
+ graph,
363
+ structuralChildren,
364
+ sizes,
365
+ );
366
+
367
+ // Phase 3: lay out all roots (chains and trees), then arrays and orphans
368
+ const result: Record<string, TraceObjectLayout> = {};
369
+ let yOffset = 0;
370
+
371
+ for (const rootId of roots) {
372
+ const rootInfo = objects.get(rootId)!;
373
+ if (chainTypes.has(rootInfo.type)) {
374
+ yOffset = layoutChainRoot(
375
+ rootId,
376
+ sizes,
377
+ refChains,
378
+ structuralChildren,
379
+ objects,
380
+ result,
381
+ yOffset,
382
+ );
383
+ } else {
384
+ yOffset = layoutTreeRoot(
385
+ rootId,
386
+ graph,
387
+ sizes,
388
+ refChains,
389
+ objectChildFields,
390
+ structuralChildren,
391
+ result,
392
+ yOffset,
393
+ );
394
+ }
395
+ }
396
+
397
+ yOffset = layoutArrays(objects, graph, sizes, fontSize, result, yOffset);
398
+ layoutOrphans(objects, sizes, result, yOffset);
399
+
400
+ return { objects: result, ignoreFields };
401
+ }
402
+
403
+ /**
404
+ * Phase 1: determine which types are "chain" (1 self-referencing field) vs
405
+ * "tree" (2+ fields), compute per-object child fields, structural children,
406
+ * and find tree/chain roots.
407
+ */
408
+ function classifyTypes(
409
+ objects: Map<string, ObjectInfo>,
410
+ fieldToChild: Map<string, Map<string, string>>,
411
+ ) {
412
+ // Find which field names are structural per type (same-type references).
310
413
  const typeChildFields = new Map<string, Set<string>>();
311
414
  for (const [parentId, fMap] of fieldToChild) {
312
415
  const parentInfo = objects.get(parentId);
@@ -324,9 +427,10 @@ export function computeLayout(
324
427
  }
325
428
  }
326
429
  }
327
- // Chain types (1 structural child field, e.g., linked list) are laid out
328
- // horizontally. Tree types (2+ fields, e.g., BST) use d3-flextree.
329
- // Only tree types get child-slot rendering.
430
+
431
+ // Chain types (1 structural child field) are laid out horizontally.
432
+ // Tree types (2+ fields) use d3-flextree. Only tree types get child-slot
433
+ // rendering.
330
434
  const chainTypes = new Set<string>();
331
435
  for (const [type, fields] of typeChildFields) {
332
436
  if (fields.size === 1) {
@@ -371,9 +475,7 @@ export function computeLayout(
371
475
  }
372
476
 
373
477
  // Find tree roots: objects that (a) belong to a type with structural child
374
- // fields (i.e., a tree type) and (b) are not themselves a structural child
375
- // of another object. Cross-type refs (e.g., PreorderIterator -> Node) don't
376
- // prevent an object from being a root.
478
+ // fields and (b) are not themselves a structural child of another object.
377
479
  const structurallyReferenced = new Set<string>();
378
480
  for (const childSet of structuralChildren.values()) {
379
481
  for (const childId of childSet) {
@@ -395,17 +497,20 @@ export function computeLayout(
395
497
  // Sort roots by first appearance for deterministic ordering
396
498
  roots.sort((a, b) => objects.get(a)!.firstStep - objects.get(b)!.firstStep);
397
499
 
398
- // Compute sizes for all objects (now with child field info)
399
- const sizes = new Map<
400
- string,
401
- { width: number; height: number; valueX?: number; cellWidth?: number }
402
- >();
403
- for (const [id, info] of objects) {
404
- const cFields = objectChildFields.get(id) ?? new Set();
405
- sizes.set(id, getObjectSize(info, fontSize, cFields));
406
- }
500
+ return { chainTypes, objectChildFields, structuralChildren, roots };
501
+ }
407
502
 
408
- // Identify tree nodes (reachable from roots via structural refs)
503
+ /**
504
+ * Phase 2: walk from roots to mark tree nodes, then compute horizontal
505
+ * ref chains for each tree node.
506
+ */
507
+ function buildRefChains(
508
+ roots: string[],
509
+ objects: Map<string, ObjectInfo>,
510
+ graph: Map<string, string[]>,
511
+ structuralChildren: Map<string, Set<string>>,
512
+ sizes: Map<string, ObjSize>,
513
+ ) {
409
514
  const treeNodeIds = new Set<string>();
410
515
  function markTreeNodes(id: string) {
411
516
  if (treeNodeIds.has(id)) {
@@ -427,7 +532,6 @@ export function computeLayout(
427
532
  }
428
533
  }
429
534
 
430
- // Compute horizontal ref chains (non-tree children laid out to the right)
431
535
  const refChains = new Map<string, RefChain>();
432
536
  const claimed = new Set<string>();
433
537
  for (const treeId of treeNodeIds) {
@@ -447,187 +551,181 @@ export function computeLayout(
447
551
  }
448
552
  }
449
553
 
450
- // Build layouts: chains go left-to-right, trees use d3-flextree
451
- const result: Record<string, TraceObjectLayout> = {};
452
- let yOffset = 0;
554
+ return { treeNodeIds, refChains };
555
+ }
453
556
 
454
- for (const rootId of roots) {
455
- const rootInfo = objects.get(rootId)!;
557
+ /** Effective width of a node including its ref chain. */
558
+ function effectiveWidth(
559
+ nodeWidth: number,
560
+ chain: RefChain | undefined,
561
+ ): number {
562
+ return nodeWidth + (chain ? Math.round(OBJ_GAP * 0.5) + chain.totalWidth : 0);
563
+ }
456
564
 
457
- if (chainTypes.has(rootInfo.type)) {
458
- // Horizontal chain layout with ref chains below each node
459
- let xOff = 0;
460
- let maxHeight = 0;
461
- let currentId: string | undefined = rootId;
462
- const visited = new Set<string>();
463
-
464
- while (currentId && !visited.has(currentId) && objects.has(currentId)) {
465
- visited.add(currentId);
466
- const size = sizes.get(currentId)!;
467
- const chain = refChains.get(currentId);
468
-
469
- // Effective width: node + ref chain to the right (if any)
470
- const nodeWidth = size.width;
471
- const chainWidth = chain
472
- ? Math.round(OBJ_GAP * 0.5) + chain.totalWidth
473
- : 0;
474
- const effectiveWidth = nodeWidth + chainWidth;
475
-
476
- const ol: TraceObjectLayout = {
477
- x: Math.round(xOff),
478
- y: Math.round(yOffset),
479
- width: size.width,
480
- height: size.height,
481
- };
482
- if (size.valueX !== undefined) {
483
- ol.valueX = size.valueX;
484
- }
485
- if (size.cellWidth !== undefined) {
486
- ol.cellWidth = size.cellWidth;
487
- }
488
- result[currentId] = ol;
489
-
490
- // Place ref chain objects to the right of this chain node
491
- if (chain) {
492
- let refX = xOff + nodeWidth + Math.round(OBJ_GAP * 0.5);
493
- for (const refId of chain.ids) {
494
- const rs = sizes.get(refId)!;
495
- const rol: TraceObjectLayout = {
496
- x: Math.round(refX),
497
- y: Math.round(yOffset),
498
- width: rs.width,
499
- height: rs.height,
500
- };
501
- if (rs.valueX !== undefined) {
502
- rol.valueX = rs.valueX;
503
- }
504
- if (rs.cellWidth !== undefined) {
505
- rol.cellWidth = rs.cellWidth;
506
- }
507
- result[refId] = rol;
508
- refX += rs.width + Math.round(OBJ_GAP * 0.5);
509
- }
510
- maxHeight = Math.max(maxHeight, size.height, chain.maxHeight);
511
- } else {
512
- maxHeight = Math.max(maxHeight, size.height);
513
- }
565
+ /** Effective height of a node including its ref chain. */
566
+ function effectiveHeight(
567
+ nodeHeight: number,
568
+ chain: RefChain | undefined,
569
+ ): number {
570
+ return chain ? Math.max(nodeHeight, chain.maxHeight) : nodeHeight;
571
+ }
514
572
 
515
- xOff += effectiveWidth + OBJ_GAP;
573
+ /**
574
+ * Place ref chain objects in a horizontal line starting at (startX, y).
575
+ * Each object in the chain is separated by half the gap.
576
+ */
577
+ function placeRefChain(
578
+ chain: RefChain,
579
+ startX: number,
580
+ y: number,
581
+ sizes: Map<string, ObjSize>,
582
+ result: Record<string, TraceObjectLayout>,
583
+ ) {
584
+ let xOff = startX;
585
+ for (const childId of chain.ids) {
586
+ const cs = sizes.get(childId)!;
587
+ result[childId] = makeLayout(xOff, y, cs);
588
+ xOff += cs.width + Math.round(OBJ_GAP * 0.5);
589
+ }
590
+ }
516
591
 
517
- // Follow the single structural child
518
- const sChildren = structuralChildren.get(currentId);
519
- currentId = sChildren ? [...sChildren][0] : undefined;
520
- }
592
+ /**
593
+ * Lay out a chain-type root and its structural children left to right.
594
+ * Returns the updated yOffset.
595
+ */
596
+ function layoutChainRoot(
597
+ rootId: string,
598
+ sizes: Map<string, ObjSize>,
599
+ refChains: Map<string, RefChain>,
600
+ structuralChildren: Map<string, Set<string>>,
601
+ objects: Map<string, ObjectInfo>,
602
+ result: Record<string, TraceObjectLayout>,
603
+ yOffset: number,
604
+ ): number {
605
+ let xOff = 0;
606
+ let maxHeight = 0;
607
+ let currentId: string | undefined = rootId;
608
+ const visited = new Set<string>();
521
609
 
522
- yOffset += maxHeight + OBJ_GAP;
523
- continue;
610
+ while (currentId && !visited.has(currentId) && objects.has(currentId)) {
611
+ visited.add(currentId);
612
+ const size = sizes.get(currentId)!;
613
+ const chain = refChains.get(currentId);
614
+ const ew = effectiveWidth(size.width, chain);
615
+
616
+ result[currentId] = makeLayout(Math.round(xOff), Math.round(yOffset), size);
617
+
618
+ if (chain) {
619
+ const refX = xOff + size.width + Math.round(OBJ_GAP * 0.5);
620
+ placeRefChain(
621
+ chain,
622
+ Math.round(refX),
623
+ Math.round(yOffset),
624
+ sizes,
625
+ result,
626
+ );
627
+ maxHeight = Math.max(maxHeight, size.height, chain.maxHeight);
628
+ } else {
629
+ maxHeight = Math.max(maxHeight, size.height);
524
630
  }
525
631
 
526
- // Tree layout with d3-flextree
527
- const visited = new Set<string>();
528
- const treeData = buildTreeNode(
529
- rootId,
530
- graph,
531
- sizes,
532
- visited,
533
- structuralChildren,
534
- );
632
+ xOff += ew + OBJ_GAP;
535
633
 
536
- if (!treeData) {
537
- continue;
538
- }
634
+ // Follow the single structural child
635
+ const sChildren = structuralChildren.get(currentId);
636
+ currentId = sChildren ? [...sChildren][0] : undefined;
637
+ }
539
638
 
540
- // d3-flextree nodeSize includes the chain width so nodes are spaced apart
541
- const layoutEngine = flextree<TreeNode>({}).nodeSize(node => {
542
- const chain = refChains.get(node.data.id);
543
- const ew =
544
- node.data.width +
545
- (chain ? Math.round(OBJ_GAP * 0.5) + chain.totalWidth : 0);
546
- const eh = chain
547
- ? Math.max(node.data.height, chain.maxHeight)
548
- : node.data.height;
549
- return [ew + OBJ_GAP, eh + OBJ_GAP];
550
- });
551
-
552
- const root = layoutEngine.hierarchy(treeData, d => d.children);
553
- layoutEngine(root);
554
-
555
- // Find bounds to translate to positive coordinates
556
- let minX = Infinity;
557
- let minY = Infinity;
558
- let maxY = -Infinity;
559
-
560
- root.each(node => {
561
- const chain = refChains.get(node.data.id);
562
- const ew =
563
- node.data.width +
564
- (chain ? Math.round(OBJ_GAP * 0.5) + chain.totalWidth : 0);
565
- const eh = chain
566
- ? Math.max(node.data.height, chain.maxHeight)
567
- : node.data.height;
568
- minX = Math.min(minX, node.x - ew / 2);
569
- minY = Math.min(minY, node.y);
570
- maxY = Math.max(maxY, node.y + eh);
571
- });
572
-
573
- // Place tree nodes and their ref chains
574
- root.each(node => {
575
- const size = sizes.get(node.data.id)!;
576
- const cFields = objectChildFields.get(node.data.id);
577
- const chain = refChains.get(node.data.id);
578
- const ew =
579
- node.data.width +
580
- (chain ? Math.round(OBJ_GAP * 0.5) + chain.totalWidth : 0);
581
-
582
- // Tree node is left-aligned within its effective bounding box
583
- const leftEdge = node.x - ew / 2;
584
-
585
- const ol: TraceObjectLayout = {
586
- x: Math.round(leftEdge - minX),
587
- y: Math.round(node.y - minY + yOffset),
588
- width: size.width,
589
- height: size.height,
590
- };
591
- if (size.valueX !== undefined) {
592
- ol.valueX = size.valueX;
593
- }
594
- if (size.cellWidth !== undefined) {
595
- ol.cellWidth = size.cellWidth;
596
- }
597
- if (cFields && cFields.size > 0) {
598
- ol.childFields = Array.from(cFields);
599
- }
600
- result[node.data.id] = ol;
601
-
602
- // Place chain objects to the right of the tree node
603
- if (chain) {
604
- let xOff =
605
- Math.round(leftEdge - minX) + size.width + Math.round(OBJ_GAP * 0.5);
606
- for (const childId of chain.ids) {
607
- const cs = sizes.get(childId)!;
608
- const col: TraceObjectLayout = {
609
- x: xOff,
610
- y: Math.round(node.y - minY + yOffset),
611
- width: cs.width,
612
- height: cs.height,
613
- };
614
- if (cs.valueX !== undefined) {
615
- col.valueX = cs.valueX;
616
- }
617
- if (cs.cellWidth !== undefined) {
618
- col.cellWidth = cs.cellWidth;
619
- }
620
- result[childId] = col;
621
- xOff += cs.width + Math.round(OBJ_GAP * 0.5);
622
- }
623
- }
624
- });
639
+ return yOffset + maxHeight + OBJ_GAP;
640
+ }
625
641
 
626
- // Stack next tree below this one
627
- yOffset += maxY - minY + OBJ_GAP;
642
+ /**
643
+ * Lay out a tree-type root using d3-flextree.
644
+ * Returns the updated yOffset.
645
+ */
646
+ function layoutTreeRoot(
647
+ rootId: string,
648
+ graph: Map<string, string[]>,
649
+ sizes: Map<string, ObjSize>,
650
+ refChains: Map<string, RefChain>,
651
+ objectChildFields: Map<string, Set<string>>,
652
+ structuralChildren: Map<string, Set<string>>,
653
+ result: Record<string, TraceObjectLayout>,
654
+ yOffset: number,
655
+ ): number {
656
+ const visited = new Set<string>();
657
+ const treeData = buildTreeNode(
658
+ rootId,
659
+ graph,
660
+ sizes,
661
+ visited,
662
+ structuralChildren,
663
+ );
664
+
665
+ if (!treeData) {
666
+ return yOffset;
628
667
  }
629
668
 
630
- // Position arrays and their referenced objects below the cells
669
+ // d3-flextree nodeSize includes the chain width so nodes are spaced apart
670
+ const layoutEngine = flextree<TreeNode>({}).nodeSize(node => {
671
+ const chain = refChains.get(node.data.id);
672
+ const ew = effectiveWidth(node.data.width, chain);
673
+ const eh = effectiveHeight(node.data.height, chain);
674
+ return [ew + OBJ_GAP, eh + OBJ_GAP];
675
+ });
676
+
677
+ const root = layoutEngine.hierarchy(treeData, d => d.children);
678
+ layoutEngine(root);
679
+
680
+ // Find bounds to translate to positive coordinates
681
+ let minX = Infinity;
682
+ let minY = Infinity;
683
+ let maxY = -Infinity;
684
+
685
+ root.each(node => {
686
+ const chain = refChains.get(node.data.id);
687
+ const ew = effectiveWidth(node.data.width, chain);
688
+ const eh = effectiveHeight(node.data.height, chain);
689
+ minX = Math.min(minX, node.x - ew / 2);
690
+ minY = Math.min(minY, node.y);
691
+ maxY = Math.max(maxY, node.y + eh);
692
+ });
693
+
694
+ // Place tree nodes and their ref chains
695
+ root.each(node => {
696
+ const size = sizes.get(node.data.id)!;
697
+ const cFields = objectChildFields.get(node.data.id);
698
+ const chain = refChains.get(node.data.id);
699
+ const ew = effectiveWidth(node.data.width, chain);
700
+
701
+ // Tree node is left-aligned within its effective bounding box
702
+ const leftEdge = node.x - ew / 2;
703
+ const nx = Math.round(leftEdge - minX);
704
+ const ny = Math.round(node.y - minY + yOffset);
705
+
706
+ result[node.data.id] = makeLayout(nx, ny, size, cFields);
707
+
708
+ if (chain) {
709
+ const chainX = nx + size.width + Math.round(OBJ_GAP * 0.5);
710
+ placeRefChain(chain, chainX, ny, sizes, result);
711
+ }
712
+ });
713
+
714
+ return yOffset + maxY - minY + OBJ_GAP;
715
+ }
716
+
717
+ /**
718
+ * Position arrays and their referenced objects below the cells.
719
+ * Returns the updated yOffset.
720
+ */
721
+ function layoutArrays(
722
+ objects: Map<string, ObjectInfo>,
723
+ graph: Map<string, string[]>,
724
+ sizes: Map<string, ObjSize>,
725
+ fontSize: number,
726
+ result: Record<string, TraceObjectLayout>,
727
+ yOffset: number,
728
+ ): number {
631
729
  for (const [id, info] of objects) {
632
730
  if (!info.isArray || id in result) {
633
731
  continue;
@@ -636,16 +734,7 @@ export function computeLayout(
636
734
  const size = sizes.get(id)!;
637
735
  const cw = size.cellWidth ?? Math.round(fontSize * CHAR_WIDTH_RATIO * 3);
638
736
 
639
- const ol: TraceObjectLayout = {
640
- x: 0,
641
- y: Math.round(yOffset),
642
- width: size.width,
643
- height: size.height,
644
- };
645
- if (size.cellWidth !== undefined) {
646
- ol.cellWidth = size.cellWidth;
647
- }
648
- result[id] = ol;
737
+ result[id] = makeLayout(0, Math.round(yOffset), size);
649
738
 
650
739
  // Get unreferenced children from graph, preserving cell order
651
740
  const children = (graph.get(id) ?? []).filter(
@@ -678,19 +767,7 @@ export function computeLayout(
678
767
  const refY = Math.round(yOffset + size.height + OBJ_GAP);
679
768
  let maxRefHeight = 0;
680
769
  for (const p of placements) {
681
- const col: TraceObjectLayout = {
682
- x: Math.round(Math.max(0, p.x)),
683
- y: refY,
684
- width: p.size.width,
685
- height: p.size.height,
686
- };
687
- if (p.size.valueX !== undefined) {
688
- col.valueX = p.size.valueX;
689
- }
690
- if (p.size.cellWidth !== undefined) {
691
- col.cellWidth = p.size.cellWidth;
692
- }
693
- result[p.id] = col;
770
+ result[p.id] = makeLayout(Math.round(Math.max(0, p.x)), refY, p.size);
694
771
  maxRefHeight = Math.max(maxRefHeight, p.height);
695
772
  }
696
773
 
@@ -699,36 +776,27 @@ export function computeLayout(
699
776
  yOffset += size.height + OBJ_GAP;
700
777
  }
701
778
  }
779
+ return yOffset;
780
+ }
702
781
 
703
- // Place any orphan objects that weren't reached by the tree/chain/array passes
782
+ /**
783
+ * Place any orphan objects that were not reached by tree, chain, or array
784
+ * layout passes.
785
+ */
786
+ function layoutOrphans(
787
+ objects: Map<string, ObjectInfo>,
788
+ sizes: Map<string, ObjSize>,
789
+ result: Record<string, TraceObjectLayout>,
790
+ yOffset: number,
791
+ ) {
792
+ let y = yOffset;
704
793
  for (const id of objects.keys()) {
705
794
  if (!(id in result)) {
706
795
  const size = sizes.get(id)!;
707
- const ol: TraceObjectLayout = {
708
- x: 0,
709
- y: Math.round(yOffset),
710
- width: size.width,
711
- height: size.height,
712
- };
713
- if (size.valueX !== undefined) {
714
- ol.valueX = size.valueX;
715
- }
716
- if (size.cellWidth !== undefined) {
717
- ol.cellWidth = size.cellWidth;
718
- }
719
- result[id] = ol;
720
- yOffset += size.height + OBJ_GAP;
796
+ result[id] = makeLayout(0, Math.round(y), size);
797
+ y += size.height + OBJ_GAP;
721
798
  }
722
799
  }
723
-
724
- return { objects: result, ignoreFields };
725
- }
726
-
727
- /** Chain of non-tree objects laid out horizontally to the right of a tree node. */
728
- interface RefChain {
729
- ids: string[];
730
- totalWidth: number;
731
- maxHeight: number;
732
800
  }
733
801
 
734
802
  /** Compute the chain of non-tree objects reachable from a tree node. */
@@ -736,10 +804,7 @@ function computeRefChain(
736
804
  startId: string,
737
805
  graph: Map<string, string[]>,
738
806
  structuralChildren: Map<string, Set<string>>,
739
- sizes: Map<
740
- string,
741
- { width: number; height: number; valueX?: number; cellWidth?: number }
742
- >,
807
+ sizes: Map<string, ObjSize>,
743
808
  treeNodeIds: Set<string>,
744
809
  claimed: Set<string>,
745
810
  ): RefChain {
@@ -808,10 +873,7 @@ function computeRefChain(
808
873
  function buildTreeNode(
809
874
  id: string,
810
875
  graph: Map<string, string[]>,
811
- sizes: Map<
812
- string,
813
- { width: number; height: number; valueX?: number; cellWidth?: number }
814
- >,
876
+ sizes: Map<string, ObjSize>,
815
877
  visited: Set<string>,
816
878
  structuralChildren: Map<string, Set<string>>,
817
879
  ): TreeNode | null {
@@ -6,12 +6,7 @@ import type {
6
6
  TraceLayout,
7
7
  TraceObjectLayout,
8
8
  } from '../types';
9
-
10
- /** Maximum number of array cells to display. */
11
- const ARRAY_MAX_CELLS = 12;
12
-
13
- /** Default font size in pixels. */
14
- const DEFAULT_FONT_SIZE = 15;
9
+ import { ARRAY_MAX_CELLS, DEFAULT_FONT_SIZE } from './trace-layout';
15
10
 
16
11
  /** Gap between stack area and heap area. */
17
12
  const SECTION_GAP = 60;