~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
129 lines · 4.2 KB Raw History
<script lang="ts">
	import type * as getPullDiff from '#lib/lexicons/dev/knotless/getPullDiff.ts';
	import type { FileDiffView } from '#lib/lexicons/dev/knotless/defs.ts';
	import type { AtUriString } from '@atproto/lex';
	import { untrack } from 'svelte';
	import { ask } from '#lib/data.ts';
	import { TREE, matched, type Source } from '#lib/diffs.ts';
	import { LaterRows } from '#lib/later.svelte.ts';
	import Icon from '#lib/atoms/Icon.svelte';
	import DiffFile, { type Notes } from '#lib/organisms/DiffFile.svelte';
	import FileTree from '#lib/organisms/FileTree.svelte';
	/**
	 * What a pull changes: a tree of its files, then each file's diff.
	 * `files` come without their rows; `shown` are those drawn with them at
	 * once, and the rest are fetched as each comes into view, but for one
	 * so large it waits to be asked for. The tree shows as `tree` says,
	 * as the viewer last chose, or where it is null by the screen's width:
	 * beside the diffs, and not on a phone. `notes` go between their lines,
	 * and the tree counts each file's `comments`. Given `source`, where
	 * the files are after it, each links there and shows more of itself as
	 * asked.
	 */
	let {
		files,
		shown,
		repo,
		uri,
		tree,
		notes,
		comments,
		source
	}: {
		files: FileDiffView[];
		shown: FileDiffView[];
		repo: AtUriString;
		uri: AtUriString;
		tree: boolean | null;
		notes?: Notes;
		comments?: (f: FileDiffView) => number;
		source?: Source;
	} = $props();
	/** The viewer's choice here, over what the page came with. */
	let chosen = $state<boolean | null>(null);
	const showing = $derived(chosen ?? tree);
	let aside = $state<HTMLElement>();

	/** Shows the tree if it is hidden, as the screen's width may have it, or hides it, and keeps that for the next pull. */
	function toggle() {
		const now = !(aside && getComputedStyle(aside).display !== 'none');
		chosen = now;
		const secure = location.protocol === 'https:' ? '; secure' : '';
		document.cookie = `${TREE}=${now ? 'shown' : 'hidden'}; path=/; max-age=31536000; samesite=lax${secure}`;
	}
	const first = $derived(
		matched(
			files,
			files.map((_, i) => i),
			shown
		)
	);
	const later = new LaterRows(
		() => files,
		() => first,
		(paths) =>
			ask<typeof getPullDiff.main>('dev.knotless.getPullDiff', { repo, uri, paths }).then(
				(a) => a.files
			)
	);
	const { rowsOf, waiting, seen, askFor } = later;

	let diffs = $state<HTMLElement>();
	let letGo: (() => void) | undefined;
	/**
	 * Keeps the diff the reader jumped to, `#diff-<i>`, at the top of the
	 * screen as the files above it are drawn and their rows come in, until
	 * they scroll themselves: WebKit holds no scroll anchor, so each file
	 * above that grows past its guessed height would push it down the page.
	 */
	function hold(i: number) {
		const target = document.getElementById(`diff-${i}`);
		if (!target || !diffs) return;
		letGo?.();
		const grown = new ResizeObserver(() => target.scrollIntoView({ block: 'start' }));
		grown.observe(diffs);
		const moved = ['wheel', 'touchstart', 'keydown', 'pointerdown'];
		const stop = () => {
			grown.disconnect();
			for (const m of moved) removeEventListener(m, stop, true);
			letGo = undefined;
		};
		for (const m of moved) addEventListener(m, stop, { capture: true, passive: true });
		letGo = stop;
	}
	/** A page opened at a file's link holds it too. */
	$effect(() => {
		const at = /^#diff-(\d+)$/.exec(location.hash);
		if (at) untrack(() => hold(Number(at[1])));
		return () => letGo?.();
	});
</script>

<div class="pull-files" class:tree-shown={showing === true} class:tree-hidden={showing === false}>
	<button
		class="btn tree-toggle"
		type="button"
		aria-label="Show or hide the file tree"
		onclick={toggle}
		><span class="hide"><Icon name="sidebarCollapse" /></span><span class="show"
			><Icon name="sidebarExpand" /></span
		></button
	>
	<aside bind:this={aside}>
		<FileTree {files} href={(i) => `#diff-${i}`} onpick={hold} {comments} />
	</aside>
	<div class="pull-files-diffs" bind:this={diffs}>
		{#each files as f, i (i)}
			<div id="diff-{i}" class="anchor" use:seen={i}>
				<DiffFile
					file={rowsOf(i) ?? f}
					waiting={waiting(i)}
					ask={() => askFor(i)}
					{notes}
					{source}
					near
				/>
			</div>
		{/each}
	</div>
</div>