This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / repo / PierreFile.svelte
2.4 kB 73 lines
1<script lang="ts"> 2 import { untrack } from "svelte"; 3 import { File, type FileOptions } from "@pierre/diffs"; 4 import { pierreFileOptions } from "./pierre"; 5 6 interface Props { 7 // used for the header and to infer the highlighting language 8 name: string; 9 contents: string; 10 // pierre's native overflow: wrap keeps highlighting instead of scrolling 11 wrap?: boolean; 12 // server-prerendered shadow dom, only set for the initial ssr 13 prerenderedHTML?: string; 14 // forwarded to pierre, fires after every (async-safe) render pass 15 onPostRender?: FileOptions<undefined>["onPostRender"]; 16 } 17 18 let { name, contents, wrap = false, prerenderedHTML, onPostRender }: Props = $props(); 19 20 let element: HTMLElement | undefined = $state(); 21 22 let instance: File | undefined; 23 24 // same manual updates as PierreDiff, this effect owns the instance, 25 // a contents swap re-renders in place instead of a shadow dom teardown 26 $effect(() => { 27 const host = element; 28 if (!host) return; 29 // scroll and wrap lay out differently enough in grid to re-hydrate on 30 // toggle, and the toggle is rare 31 const options: FileOptions<undefined> = { 32 ...pierreFileOptions(wrap), 33 onPostRender 34 }; 35 36 // sveltekit re-runs the load on hydration, the prop never reaches the 37 // client. pierre marks its ssr output with data-dehydrated 38 const dehydrated = host.shadowRoot?.querySelector("[data-dehydrated]") != null; 39 if (prerenderedHTML === undefined && !dehydrated) host.shadowRoot?.replaceChildren(); 40 const created = new File(options, undefined, true); 41 // untracked, the render effect below owns updates 42 created.hydrate({ 43 file: untrack(() => ({ name, contents })), 44 fileContainer: host, 45 prerenderedHTML 46 }); 47 instance = created; 48 49 return () => { 50 created.cleanUp(); 51 instance = undefined; 52 }; 53 }); 54 55 // hydrate paints the initial file, this pass covers later name/contents 56 // changes 57 let firstRender = true; 58 $effect(() => { 59 const file = { name, contents }; 60 if (firstRender) { 61 firstRender = false; 62 return; 63 } 64 instance?.render({ file, forceRender: true }); 65 }); 66</script> 67 68<diffs-container bind:this={element} class="diffs-container"> 69 {#if prerenderedHTML} 70 <!-- pierre's own renderer output, token text is escaped by its hast builder --> 71 <template shadowrootmode="open">{@html prerenderedHTML}</template> 72 {/if} 73</diffs-container>