Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128<script lang="ts"> import { untrack } from "svelte"; import { File, type FileOptions, type SelectedLineRange } from "@pierre/diffs"; import { browser } from "$app/env"; import { clearPierreRender, pierreFileOptions, prerenderFile } from "#lib/components/repo/pierre.ts"; import BlameOverlay from "#lib/components/repo/blame/BlameOverlay.svelte"; import LineHandles from "#lib/components/repo/LineHandles.svelte"; import { BlameHover } from "#lib/components/repo/blame/hover.svelte.ts"; import type { Blame } from "#lib/components/repo/blame/types.ts";
interface Props { // used for the header and to infer the highlighting language name: string; contents: string; // pierre's native overflow: wrap keeps highlighting instead of scrolling wrap?: boolean; // forwarded to pierre, fires after every (async-safe) render pass onPostRender?: FileOptions<undefined, undefined>["onPostRender"]; // one run per stretch of lines sharing a commit, absent when blame is off blame?: Blame; selectedLines?: SelectedLineRange | null; onLineSelected?: (range: SelectedLineRange | null) => void; }
let { name, contents, wrap = false, onPostRender, blame, selectedLines = null, onLineSelected }: Props = $props();
let element: HTMLElement | undefined = $state(); let wrapper: HTMLElement | undefined = $state();
const hover = new BlameHover(() => wrapper);
const options = (forWrap: boolean): FileOptions<undefined, undefined> => ({ ...pierreFileOptions(forWrap), onPostRender, enableLineSelection: onLineSelected !== undefined, onLineSelected: (range) => onLineSelected?.(range), onLineEnter: ({ lineNumber, lineElement, event }) => { if (!blame || event.pointerType === "touch") return; hover.enter(lineNumber, lineElement); }, onLineLeave: () => hover.leave() });
$effect(() => { if (!blame) hover.hide(); });
let instance: File | undefined;
// same manual updates as PierreDiff, this effect owns the instance, // a contents swap re-renders in place instead of a shadow dom teardown $effect(() => { const host = element; if (!host) return; // scroll and wrap lay out differently enough in grid to re-hydrate on // toggle, and the toggle is rare const dehydrated = host.shadowRoot?.querySelector("[data-dehydrated]") != null; if (!dehydrated) clearPierreRender(host); // pierre hands its re-renders (a finished highlight) to the owner const created: File = new File(options(wrap), undefined, () => created.render({ file: untrack(() => ({ name, contents })), forceRender: true }) ); // untracked, the render effect below owns updates untrack(() => { created.hydrate({ file: { name, contents }, fileContainer: host }); created.setSelectedLines(selectedLines, { notify: false }); }); instance = created;
return () => { created.cleanUp(); instance = undefined; }; });
// hydrate paints the initial file, this pass covers later name/contents // changes let firstRender = true; $effect(() => { const file = { name, contents }; if (firstRender) { firstRender = false; return; } untrack(() => instance?.render({ file, forceRender: true })); });
$effect(() => { const range = selectedLines; instance?.setSelectedLines(range, { notify: false }); });</script>
<div bind:this={wrapper} class="relative isolate"> <diffs-container data-testid="fileContents" data-filename={name} bind:this={element} class="diffs-container [--diffs-gap-style:none] [--diffs-selection-base:var(--color-indigo-500)]" > {#if !browser} {@const prerenderedHTML = await prerenderFile(name, contents)} {#if prerenderedHTML} <!-- eslint-disable-next-line svelte/no-at-html-tags -- pierre's own renderer output, token text is escaped by its hast builder --> <template shadowrootmode="open">{@html prerenderedHTML}</template> {/if} {/if} </diffs-container> {#if blame} <BlameOverlay {blame} {hover} /> {/if} {#if onLineSelected} <LineHandles container={wrapper} /> {/if}</div>