|
14 | 14 | type SearchSegment, |
15 | 15 | } from "$lib/components/diff/concise-diff-view.svelte"; |
16 | 16 | import Spinner from "$lib/components/Spinner.svelte"; |
17 | | - import { onMount } from "svelte"; |
18 | 17 | import { type MutableValue } from "$lib/util"; |
19 | 18 | import { box } from "svelte-toolbelt"; |
20 | 19 | import { boolAttr } from "runed"; |
|
124 | 123 | const endIdx = selection.end.idx; |
125 | 124 | return Math.floor((startIdx + endIdx) / 2); |
126 | 125 | }); |
| 126 | +
|
| 127 | + let heightEstimateRem = $derived.by(() => { |
| 128 | + if (!parsedPatch) return 1.25; |
| 129 | + const rawLineCount = parsedPatch.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0); |
| 130 | + const headerAndSpacerLines = parsedPatch.hunks.length * 2; |
| 131 | + const totalLines = rawLineCount + headerAndSpacerLines; |
| 132 | + return totalLines * 1.25; |
| 133 | + }); |
127 | 134 | </script> |
128 | 135 |
|
129 | 136 | {#snippet lineContent(line: PatchLine, lineType: PatchLineTypeProps, innerLineType: InnerPatchLineTypeProps)} |
|
156 | 163 | {#each lineSearchSegments as searchSegment, index (index)} |
157 | 164 | {#if searchSegment.highlighted}<span |
158 | 165 | {@attach (element) => { |
159 | | - onMount(() => { |
160 | | - if (jumpToSearchResult && searchSegment.id === activeSearchResult) { |
161 | | - jumpToSearchResult = false; |
162 | | - // See similar code & comment below around jumping to selections |
163 | | - const scheduledJump = setTimeout(() => { |
164 | | - element.scrollIntoView({ block: "center", inline: "center" }); |
165 | | - }, 100); |
166 | | - return () => { |
167 | | - clearTimeout(scheduledJump); |
168 | | - }; |
169 | | - } |
170 | | - }); |
| 166 | + if (jumpToSearchResult && searchSegment.id === activeSearchResult) { |
| 167 | + jumpToSearchResult = false; |
| 168 | + // See similar code & comment below around jumping to selections |
| 169 | + const scheduledJump = setTimeout(() => { |
| 170 | + element.scrollIntoView({ block: "center", inline: "center" }); |
| 171 | + }, 200); |
| 172 | + return () => { |
| 173 | + clearTimeout(scheduledJump); |
| 174 | + }; |
| 175 | + } |
171 | 176 | }} |
172 | 177 | class={{ |
173 | 178 | "bg-[#d4a72c66]": searchSegment.id !== activeSearchResult, |
|
209 | 214 | data-selection-start={boolAttr(view.isSelectionStart(hunkIndex, lineIndex))} |
210 | 215 | data-selection-end={boolAttr(view.isSelectionEnd(hunkIndex, lineIndex))} |
211 | 216 | {@attach (element) => { |
212 | | - onMount(() => { |
213 | | - if (jumpToSelection && selection && selection.hunk === hunkIndex && selectionMidpoint === lineIndex) { |
214 | | - jumpToSelection = false; |
215 | | - // Need to schedule because otherwise the vlist rendering surrounding elements may shift things |
216 | | - // and cause the element to scroll to the wrong position |
217 | | - // This is not 100% reliable but is good enough for now |
218 | | - const scheduledJump = setTimeout(() => { |
219 | | - element.scrollIntoView({ block: "center", inline: "center" }); |
220 | | - }, 200); |
221 | | - return () => { |
222 | | - if (scheduledJump) { |
223 | | - clearTimeout(scheduledJump); |
224 | | - } |
225 | | - }; |
226 | | - } |
227 | | - }); |
| 217 | + if (jumpToSelection && selection && selection.hunk === hunkIndex && selectionMidpoint === lineIndex) { |
| 218 | + jumpToSelection = false; |
| 219 | + // Need to schedule because otherwise the vlist rendering surrounding elements may shift things |
| 220 | + // and cause the element to scroll to the wrong position |
| 221 | + // This is not 100% reliable but is good enough for now |
| 222 | + const scheduledJump = setTimeout(() => { |
| 223 | + element.scrollIntoView({ block: "center", inline: "center" }); |
| 224 | + }, 200); |
| 225 | + return () => { |
| 226 | + if (scheduledJump) { |
| 227 | + clearTimeout(scheduledJump); |
| 228 | + } |
| 229 | + }; |
| 230 | + } |
228 | 231 | }} |
229 | 232 | > |
230 | 233 | {@render lineContentWrapper(line, hunkIndex, lineIndex, lineType, innerPatchLineTypeProps[line.innerPatchLineType])} |
231 | 234 | </div> |
232 | 235 | {/snippet} |
233 | 236 |
|
234 | 237 | {#await Promise.all([view.rootStyle, view.diffViewerPatch])} |
235 | | - <div class="flex items-center justify-center bg-neutral-2 p-4"><Spinner /></div> |
| 238 | + <div class="relative bg-neutral-2" style="min-height: {heightEstimateRem}rem;"> |
| 239 | + <!-- 2.25 rem for file header offset --> |
| 240 | + <div class="sticky top-[2.25rem] flex items-center justify-center p-4"> |
| 241 | + <Spinner /> |
| 242 | + </div> |
| 243 | + </div> |
236 | 244 | {:then [rootStyle, diffViewerPatch]} |
237 | 245 | <div |
238 | 246 | id={uid} |
|
271 | 279 |
|
272 | 280 | display: grid; |
273 | 281 | grid-template-columns: min-content min-content auto; |
274 | | - contain: layout style paint; |
275 | 282 | } |
276 | 283 | .diff-content[data-wrap="true"] { |
277 | 284 | word-break: break-all; |
|
0 commit comments