diff --git a/ui/src/components/symbol/CalleeRail.svelte b/ui/src/components/symbol/CalleeRail.svelte index 6afeca8..b25f913 100644 --- a/ui/src/components/symbol/CalleeRail.svelte +++ b/ui/src/components/symbol/CalleeRail.svelte @@ -22,6 +22,8 @@ tops: number[]; foldTop: number; noteTop: number; + /** False until the view has measured the rail — see SymbolView. */ + placed: boolean; /** The focal symbol's file — a callee in it reads "same file", not a path. */ focalFile: string; /** The symbol this one was reached from, when it is a callee. */ @@ -31,7 +33,7 @@ onstepDown: (node: WireNodeRef) => void; } - let { model, tops, foldTop, noteTop, focalFile, originId, emptyReason, onstepDown }: Props = + let { model, tops, foldTop, noteTop, placed, focalFile, originId, emptyReason, onstepDown }: Props = $props(); function rowTitle(row: CalleeRow): string { @@ -51,6 +53,7 @@ class:origin={node.id === originId} class:hot={hot.is(node.id)} class:sel={railFocus.at('right', i)} + class:unplaced={!placed} style:top={`${tops[i] ?? 0}px`} data-target={node.id} role="button" @@ -84,7 +87,7 @@ {/each} {#if model.uncertain.length > 0} -
+
Uncertain · {model.uncertain.length} name-only match{model.uncertain.length === 1 ? '' : 'es'}, @@ -128,7 +131,7 @@ {#if model.rows.length === 0 && model.uncertain.length === 0}
{emptyReason}
{:else if model.outsideCalls > 0 || model.outsideTypeRefs > 0 || model.hiddenGroups > 0} -
+
{#if model.outsideCalls > 0} +{plural(model.outsideCalls, 'more call')} into symbols outside the index{#if model.outsideTypeRefs > 0}{' '}· {plural(model.outsideTypeRefs, 'type reference')}{/if}. @@ -167,6 +170,11 @@ font-size: 11.5px; } + /* Positioned by measurement, so it must not paint before it is measured. */ + .unplaced { + visibility: hidden; + } + .rrow { position: absolute; right: 12px; diff --git a/ui/src/views/SymbolView.svelte b/ui/src/views/SymbolView.svelte index bc56012..c3f3736 100644 --- a/ui/src/views/SymbolView.svelte +++ b/ui/src/views/SymbolView.svelte @@ -73,6 +73,14 @@ let stageMinHeight = $state(0); let connectors = $state([]); let overlay = $state({ width: 0, height: 0 }); + /** + * The rail has been measured at least once for the symbol on screen. + * + * Until it has, a row has no place to be: drawing it at `top: 0` would stack + * every row at the head of the rail for a frame, and drawing it at the + * PREVIOUS symbol's coordinates would be worse. It stays hidden instead. + */ + let placed = $state(false); /* ---------------------------------------------------------------- data -- */ @@ -90,6 +98,7 @@ source = null; railFocus.reset(); hot.set(null); + placed = false; void project.ensure(); let node: WireSymbolPayload; @@ -322,6 +331,7 @@ }); connectors = next; overlay = { width: inner.scrollWidth, height: Math.max(inner.offsetHeight, stageMinHeight) }; + placed = true; } let scheduled = false; @@ -460,6 +470,7 @@ {tops} {foldTop} {noteTop} + {placed} focalFile={payload.node.file} originId={originRight} emptyReason={emptyCalleeReason}