diff --git a/.gitignore b/.gitignore index 95494dfdd..36eee0a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ id_ed25519 *.key .claude/ .playwright-mcp/ + +.browser-artifacts diff --git a/knotmirror/xrpc/git_format_patch.go b/knotmirror/xrpc/git_format_patch.go index 4386dcf09..a63a5eee1 100644 --- a/knotmirror/xrpc/git_format_patch.go +++ b/knotmirror/xrpc/git_format_patch.go @@ -46,6 +46,8 @@ func (x *Xrpc) FormatPatch(w http.ResponseWriter, r *http.Request) { cmd.Stderr = stderr w.Header().Set("Content-Type", "text/plain; charset=utf-8") + // a format-patch range is immutable by commit sha, so edges may cache it + w.Header().Set("Cache-Control", "public, max-age=3600, s-maxage=86400, immutable") cmd.Stdout = w if err := cmd.Run(); err != nil { l.Error("running format-patch", "err", err, "stderr", stderr.String()) diff --git a/web/.gitignore b/web/.gitignore index f3e2e6f49..c7a5fd598 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -33,3 +33,5 @@ storybook-static !patches/ .eslintcache + +.browser-artifacts diff --git a/web/boneyard.config.json b/web/boneyard.config.json index 58e46a0cb..8f63610ee 100644 --- a/web/boneyard.config.json +++ b/web/boneyard.config.json @@ -139,9 +139,6 @@ "timeline-notificationspreview--skeleton-fixture": { "route": "/iframe.html?id=timeline-notificationspreview--skeleton-fixture&viewMode=story" }, - "repo-diffview--skeleton-fixture": { - "route": "/iframe.html?id=repo-diffview--skeleton-fixture&viewMode=story" - }, "repo-pulls-pulldiff--skeleton-fixture": { "route": "/iframe.html?id=repo-pulls-pulldiff--skeleton-fixture&viewMode=story" }, diff --git a/web/package.json b/web/package.json index 2066cfbda..62d1343ee 100644 --- a/web/package.json +++ b/web/package.json @@ -6,7 +6,7 @@ "packageManager": "pnpm@11.17.0", "scripts": { "dev": "vite dev", - "build": "vite build", + "build": "vite build && node scripts/asset-headers.ts", "preview": "vite preview --host 0.0.0.0", "deploy:cloudflare-dev": "./scripts/deploy-cloudflare-dev.sh", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", diff --git a/web/patches/@pierre__diffs.patch b/web/patches/@pierre__diffs.patch new file mode 100644 index 000000000..15d70fdb8 --- /dev/null +++ b/web/patches/@pierre__diffs.patch @@ -0,0 +1,50 @@ +diff --git a/dist/components/VirtualizedFile.js b/dist/components/VirtualizedFile.js +index abb195e6bf38e3a8f1e5c72f630f9c9809554774..45bc44ff026e3cb2c3020611e864acd2ea216934 100644 +--- a/dist/components/VirtualizedFile.js ++++ b/dist/components/VirtualizedFile.js +@@ -431,12 +431,17 @@ var VirtualizedFile = class extends File { + const startingLine = Math.max(0, Math.min(idealStartHunk, totalHunksInFile)) * hunkLineCount; + const clampedTotalLines = idealStartHunk < 0 ? totalLines + idealStartHunk * hunkLineCount : totalLines; + const bufferBefore = startingLine === 0 ? 0 : fileAnnotationHeight + startingLine * lineHeight; +- const renderedLines = Math.min(clampedTotalLines, lineCount - startingLine); ++ // tangled patch: the hunk-centered window under-allocates at the very end of ++ // content — render every remaining line when the item ends at the window bottom ++ const atContentEnd = sourceRowsBottom <= bottom + lineHeight; ++ const renderedLines = atContentEnd ++ ? lineCount - startingLine ++ : Math.min(clampedTotalLines, lineCount - startingLine); + return { + startingLine, +- totalLines: clampedTotalLines, ++ totalLines: atContentEnd ? lineCount - startingLine : clampedTotalLines, + bufferBefore, +- bufferAfter: Math.max(0, (lineCount - startingLine - renderedLines) * lineHeight) ++ bufferAfter: atContentEnd ? 0 : Math.max(0, (lineCount - startingLine - renderedLines) * lineHeight) + }; + } + const overflowHunks = totalHunks; +diff --git a/dist/components/VirtualizedFileDiff.js b/dist/components/VirtualizedFileDiff.js +index 3d48b53247ae3bd1f8b8c2b668efe084d723cdf5..c80c91b9c118f79f4826e64cafe65ff755c2da2d 100644 +--- a/dist/components/VirtualizedFileDiff.js ++++ b/dist/components/VirtualizedFileDiff.js +@@ -734,13 +734,18 @@ var VirtualizedFileDiff = class extends FileDiff { + const clampedTotalLines = idealStartHunk < 0 ? totalLines + idealStartHunk * hunkLineCount : totalLines; + const codeBufferBefore = hunkOffsets[startHunk] ?? 0; + const bufferBefore = startingLine === 0 ? 0 : fileAnnotationHeight + codeBufferBefore; ++ // tangled patch: at the very end of content the hunk-centered window ++ // under-allocates and leaves the final lines permanently unrendered at max ++ // scroll (12M+ px mega-diffs). when the item's rows end within a line-height ++ // of the window bottom, render every remaining line. ++ const atContentEnd = fileTop + codeRegionTop + codeHeight <= bottom + lineHeight; + const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount; + const bufferAfter = finalHunkIndex < hunkOffsets.length ? codeHeight - hunkOffsets[finalHunkIndex] : codeHeight - (absoluteLineTop - fileTop - codeRegionTop); + return { + startingLine, +- totalLines: clampedTotalLines, ++ totalLines: atContentEnd ? lineCount - startingLine : clampedTotalLines, + bufferBefore, +- bufferAfter: Math.max(0, bufferAfter) ++ bufferAfter: atContentEnd ? 0 : Math.max(0, bufferAfter) + }; + } + }; diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index fb5d23cb4..d4bb9d4ce 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -5,6 +5,7 @@ settings: excludeLinksFromLockfile: false patchedDependencies: + '@pierre/diffs': 81549e7b5525627e265441b26345e5ba9aa0ff1ffc0a91570f9e169dc9343349 boneyard-js@1.9.0: e9bda6b0720bed34eefee605bd041784ac2849ae4d27120d9815663d342f694f importers: @@ -46,7 +47,7 @@ importers: version: 1.0.1(markdown-it@14.3.0) '@pierre/diffs': specifier: ^1.2.12 - version: 1.2.12(@shikijs/themes@4.3.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.2.12(patch_hash=81549e7b5525627e265441b26345e5ba9aa0ff1ffc0a91570f9e169dc9343349)(@shikijs/themes@4.3.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@tanstack/svelte-virtual': specifier: ^3.13.35 version: 3.13.35(svelte@5.56.9(@typescript-eslint/types@8.64.0)) @@ -4355,7 +4356,7 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@pierre/diffs@1.2.12(@shikijs/themes@4.3.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@pierre/diffs@1.2.12(patch_hash=81549e7b5525627e265441b26345e5ba9aa0ff1ffc0a91570f9e169dc9343349)(@shikijs/themes@4.3.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@pierre/theme': 1.1.0 '@pierre/theming': 0.0.2(@pierre/theme@1.1.0)(@shikijs/themes@4.3.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(shiki@4.3.1) diff --git a/web/pnpm-workspace.yaml b/web/pnpm-workspace.yaml index 3649e9cbf..01400475f 100644 --- a/web/pnpm-workspace.yaml +++ b/web/pnpm-workspace.yaml @@ -2,4 +2,5 @@ allowBuilds: esbuild: true workerd: true patchedDependencies: + "@pierre/diffs": patches/@pierre__diffs.patch boneyard-js@1.9.0: patches/boneyard-js@1.9.0.patch diff --git a/web/scripts/asset-headers.ts b/web/scripts/asset-headers.ts new file mode 100644 index 000000000..b9cca5ff3 --- /dev/null +++ b/web/scripts/asset-headers.ts @@ -0,0 +1,13 @@ +#!/usr/bin/env node +// the cloudflare adapter writes _headers during `vite build`, so this runs +// after it and rewrites what it left. a node build has no such file: nothing to +// do, and nothing to fail on +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { hardenAssetHeaders } from "../src/lib/assetHeaders.ts"; + +const path = ".svelte-kit/cloudflare/_headers"; +if (existsSync(path)) { + const hardened = hardenAssetHeaders(readFileSync(path, "utf8")); + writeFileSync(path, hardened); + console.log("asset-headers: immutable misses now revalidate"); +} diff --git a/web/src/app.css b/web/src/app.css index 32691959a..26066e999 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -14,6 +14,17 @@ } } +/* UA widgets - scrollbars first among them - paint from color-scheme, not from + our var theme, so any scroller would carry a light system track over a dark + surface. one declaration at the root keeps every scrollbar - file tree, code + scroller, rails - matching the surface it sits on */ +html { + color-scheme: light; + @variant dark { + color-scheme: dark; + } +} + @theme { --font-sans: "InterVariable", "system-ui", sans-serif, ui-sans-serif; --font-mono: diff --git a/web/src/app.d.ts b/web/src/app.d.ts index 79f9fdee7..265a3c023 100644 --- a/web/src/app.d.ts +++ b/web/src/app.d.ts @@ -10,12 +10,18 @@ declare global { moderationServiceDid: string; turnstileSiteKey: string; camoEnabled: boolean; + // when the server defers the diff fetch to the browser + ssrDiffMaxBytes: number; }; } interface Platform { // worker service bindings, only present when running on Cloudflare env?: { + // diff payloads above this many bytes are not buffered through the + // ssr worker; the page refetches them from the browser instead. + // "0" disables. defaults to 5 MiB + SSR_DIFF_MAX_BYTES?: string; BOBBIN?: { fetch(request: Request): Promise }; // verified email → did lookup, populated once by cmd/email-did-migrate EMAIL_DID?: { get(key: string): Promise }; diff --git a/web/src/fixtures/pull.ts b/web/src/fixtures/pull.ts index 42e831af3..201834181 100644 --- a/web/src/fixtures/pull.ts +++ b/web/src/fixtures/pull.ts @@ -325,8 +325,20 @@ export const mockPullFetch = (options: PullFetchOptions = {}): typeof fetch => case "sh.tangled.git.temp2.mergeCheck": return json({ isConflicted: !!options.conflicted, + // two files, so a conflict list exercises both the file the diff + // opens on and one the reader has to be carried to conflicts: options.conflicted - ? [{ filename: "src/lib/notifications.svelte.ts", reason: "both modified" }] + ? [ + { + filename: "src/lib/notifications.svelte.ts", + reason: "both modified" + }, + { + filename: + "src/lib/components/notifications/NotificationList.svelte", + reason: "both added" + } + ] : [] }); diff --git a/web/src/lib/api/diff.ts b/web/src/lib/api/diff.ts index 782d0b448..3c86cc9be 100644 --- a/web/src/lib/api/diff.ts +++ b/web/src/lib/api/diff.ts @@ -23,6 +23,12 @@ export interface DiffStat { files_changed: number; } +export interface LineStat { + insertions: number; + deletions: number; + files_changed?: number; +} + export const LineOp = { Context: 0, Delete: 1, Add: 2 } as const; export type LineOp = (typeof LineOp)[keyof typeof LineOp]; diff --git a/web/src/lib/api/knotmirror.ts b/web/src/lib/api/knotmirror.ts index 1417211cd..090fae2a1 100644 --- a/web/src/lib/api/knotmirror.ts +++ b/web/src/lib/api/knotmirror.ts @@ -15,6 +15,7 @@ import { const TREE_NSID = "sh.tangled.git.temp.getTree"; const ENTRY_NSID = "sh.tangled.git.temp.getEntry"; const BLOB_NSID = "sh.tangled.git.temp.getBlob"; +const FORMAT_PATCH_NSID = "sh.tangled.git.temp.formatPatch"; const LOG_NSID = "sh.tangled.git.temp.listCommits"; const BRANCHES_NSID = "sh.tangled.git.temp.listBranches"; const TAGS_NSID = "sh.tangled.git.temp.listTags"; @@ -58,6 +59,14 @@ export const blobRawUrl = ( params: { repo: string; ref: string; path: string } ): string => buildUrl(serviceUrl, BLOB_NSID, params).toString(); +// the mirror streams `git format-patch --stdout` for a range, which is the only +// way to get a large diff in one response: the knot builds the whole thing as +// json first and refuses anything past its 5MB response ceiling +export const formatPatchUrl = ( + serviceUrl: string, + params: { repo: string; from: string; to: string } +): string => buildUrl(serviceUrl, FORMAT_PATCH_NSID, params).toString(); + // same heuristic as git: a null byte in the first chunk means binary const looksBinary = (bytes: Uint8Array): boolean => bytes.subarray(0, 8000).includes(0); diff --git a/web/src/lib/api/patchStream.test.ts b/web/src/lib/api/patchStream.test.ts new file mode 100644 index 000000000..69ae8b502 --- /dev/null +++ b/web/src/lib/api/patchStream.test.ts @@ -0,0 +1,77 @@ +import type { FileDiffMetadata } from "@pierre/diffs"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { getPatchStream, startPatchStream } from "$lib/api/patchStream"; + +const streamPatchInWorker = vi.hoisted(() => vi.fn()); + +vi.mock("$lib/components/repo/diffRpc", () => ({ streamPatchInWorker })); + +const file = (name: string, prevName?: string) => + ({ name, prevName, hunks: [] }) as unknown as FileDiffMetadata; + +describe("startPatchStream", () => { + beforeEach(() => { + streamPatchInWorker.mockReset(); + }); + + it("indexes every landed file by its name and by the name it had before", async () => { + streamPatchInWorker.mockImplementation( + async (_url: string, land: (files: FileDiffMetadata[]) => void) => { + land([file("src/new.ts", "src/old.ts")]); + } + ); + + const stream = startPatchStream("/patch"); + + await expect(stream.done).resolves.toBe(true); + expect(stream.byName.get("src/new.ts")).toBe(stream.byName.get("src/old.ts")); + }); + + it("reports a failed patch instead of throwing at whoever waits for it", async () => { + streamPatchInWorker.mockRejectedValue(new Error("mirror is down")); + + await expect(startPatchStream("/patch").done).resolves.toBe(false); + }); + + it("drops batches that land after the reader walked away", async () => { + let land: (files: FileDiffMetadata[]) => void = () => undefined; + streamPatchInWorker.mockImplementation( + (_url: string, listener: (files: FileDiffMetadata[]) => void) => { + land = listener; + return new Promise(() => undefined); + } + ); + + const stream = startPatchStream("/patch"); + const seen: FileDiffMetadata[][] = []; + stream.subscribe((files) => seen.push(files)); + + stream.stop(); + land([file("src/late.ts")]); + + expect(seen).toEqual([]); + expect(stream.byName.size).toBe(0); + }); +}); + +describe("getPatchStream", () => { + beforeEach(() => { + streamPatchInWorker.mockReset(); + streamPatchInWorker.mockImplementation(() => new Promise(() => undefined)); + }); + + it("shares one download between every caller asking for the same patch", () => { + expect(getPatchStream("/shared")).toBe(getPatchStream("/shared")); + }); + + it("keeps the last couple of patches and stops the ones before them", () => { + const first = getPatchStream("/first"); + const stopped = vi.spyOn(first, "stop"); + + getPatchStream("/second"); + getPatchStream("/third"); + + expect(stopped).toHaveBeenCalled(); + expect(getPatchStream("/first")).not.toBe(first); + }); +}); diff --git a/web/src/lib/api/patchStream.ts b/web/src/lib/api/patchStream.ts new file mode 100644 index 000000000..d174ff267 --- /dev/null +++ b/web/src/lib/api/patchStream.ts @@ -0,0 +1,73 @@ +import type { FileDiffMetadata } from "@pierre/diffs"; +import { streamPatchInWorker } from "$lib/components/repo/diffRpc"; + +/** + * A patch being parsed in the worker while it downloads. Starting one does not + * wait for anything else on the page: the file list and the patch describe the + * same commit, and fetching them one after the other costs the sum of both. + */ +export interface PatchStream { + /** parsed files, keyed by current name and by previous name for renames */ + readonly byName: Map; + /** called with each batch as it lands, and unsubscribed on teardown */ + subscribe: (listener: (files: FileDiffMetadata[]) => void) => () => void; + /** false when the patch could not be read, so a caller can fall back */ + readonly done: Promise; + stop: () => void; +} + +// Dedupes in-flight patch streams by URL. +const live = new Map(); +const KEEP = 2; + +export const getPatchStream = (url: string): PatchStream => { + const held = live.get(url); + if (held) return held; + const stream = startPatchStream(url); + live.set(url, stream); + // a navigation leaves its patch behind; keeping the last couple means going + // back to a commit is instant, and anything older is dropped + for (const [key, value] of live) { + if (live.size <= KEEP) break; + value.stop(); + live.delete(key); + } + return stream; +}; + +export const startPatchStream = (url: string): PatchStream => { + const byName = new Map(); + const listeners = new Set<(files: FileDiffMetadata[]) => void>(); + const controller = new AbortController(); + let stopped = false; + + const land = (files: FileDiffMetadata[]) => { + if (stopped) return; + for (const file of files) { + byName.set(file.name, file); + if (file.prevName) byName.set(file.prevName, file); + } + for (const listener of listeners) listener(files); + }; + + const stream: PatchStream = { + byName, + subscribe: (listener) => { + listeners.add(listener); + return () => listeners.delete(listener); + }, + // resolved either way: a caller that never asks cannot leave a rejection + // unhandled, and one that does gets an answer instead of an exception + done: streamPatchInWorker(url, land, controller.signal).then( + () => true, + () => false + ), + stop: () => { + stopped = true; + listeners.clear(); + controller.abort(); + } + }; + + return stream; +}; diff --git a/web/src/lib/api/pullDiff.test.ts b/web/src/lib/api/pullDiff.test.ts index 5eef0691d..c6c6c0c39 100644 --- a/web/src/lib/api/pullDiff.test.ts +++ b/web/src/lib/api/pullDiff.test.ts @@ -1,7 +1,15 @@ import { describe, expect, it, vi } from "vitest"; import { createBobbinClient } from "$lib/api/client"; import { gitTarget } from "$lib/api/gitclient"; -import { fetchSides, loadDiff, resolveChange } from "$lib/api/pullDiff"; +import { + cappedFetch, + DiffTooLargeError, + isDiffTooLarge, + fetchSides, + loadDiff, + resolveChange, + type PullDiffFile +} from "$lib/api/pullDiff"; import type { MirrorFileDiff } from "$lib/api/gitmirror"; const src = (path: string, extra: Partial = {}) => ({ @@ -99,7 +107,6 @@ describe("loadDiff", () => { const diff = await loadDiff(deps, diffSpec); expect(diff.stat).toEqual({ insertions: 3, deletions: 2, files_changed: 4 }); - expect(diff.truncated).toBe(0); expect(diff.contents).toEqual({ oldRef: "base", newRef: "head" }); expect(diff.files.map((file) => file.kind)).toEqual([ "changed", @@ -341,3 +348,47 @@ describe("resolveChange", () => { }); }); }); + +describe("cappedFetch", () => { + const bigJson = (bytes: number) => + new Response("x".repeat(bytes), { + status: 200, + headers: { "content-type": "application/json" } + }); + + it("passes through bodies under the cap", async () => { + const capped = cappedFetch( + vi.fn(async () => json({ ok: true })), + 1 << 20 + ); + const res = await capped("https://mirror.test/xrpc/nsid"); + await expect(res.json()).resolves.toEqual({ ok: true }); + }); + + it("fails fast once the body passes the cap", async () => { + const capped = cappedFetch( + vi.fn(async () => bigJson(4096)), + 1024 + ); + const res = await capped("https://mirror.test/xrpc/nsid"); + await expect(res.json()).rejects.toThrow(DiffTooLargeError); + }); + + it("is detectable through isDiffTooLarge", async () => { + const capped = cappedFetch( + vi.fn(async () => bigJson(4096)), + 1024 + ); + const res = await capped("https://mirror.test/xrpc/nsid"); + await expect(res.json().catch(isDiffTooLarge)).resolves.toBe(true); + }); + + it("does not touch non-ok responses", async () => { + const capped = cappedFetch( + vi.fn(async () => new Response("no", { status: 500 })), + 8 + ); + const res = await capped("https://mirror.test/xrpc/nsid"); + expect(res.status).toBe(500); + }); +}); diff --git a/web/src/lib/api/pullDiff.ts b/web/src/lib/api/pullDiff.ts index a360a748f..23511f340 100644 --- a/web/src/lib/api/pullDiff.ts +++ b/web/src/lib/api/pullDiff.ts @@ -21,10 +21,6 @@ import type { CommitSummary } from "$lib/api/repo"; type DiffSrc = ShTangledGitDefs.DiffSrc; -// a sanity ceiling, not a render budget: contents are fetched per card on demand, -// so this only stops a pathological diff from queueing unbounded work -const FILE_LIMIT = 300; - // pierre renders roughly one row per line pair, plus a little chrome per hunk. only // used to reserve height under a pending card, so an estimate is enough const HUNK_CHROME_LINES = 2; @@ -64,8 +60,7 @@ export interface PullDiffFile { stat: { insertions: number; deletions: number }; // rough rendered height, so a card can reserve space before its text lands lines: number; - // set when there is nothing to render: binary, submodule, too large, or past - // the ceiling. a file with a note has no `sides` to fetch + // Explains unrenderable files (binary, submodule, oversized); suppresses sides fetch. note?: string; sides?: { lhs: DiffSrc; rhs: DiffSrc }; } @@ -79,8 +74,6 @@ export type ContentsSource = export interface PullDiff { files: PullDiffFile[]; stat: DiffStat; - // how many files were listed but left unrendered by the ceiling - truncated: number; contents: ContentsSource; } @@ -92,6 +85,50 @@ export interface FilePair { export type FileContentsResult = FilePair | { note: string }; +export const DIFF_TOO_LARGE_MESSAGE = "diff payload exceeds the ssr byte cap"; + +export class DiffTooLargeError extends Error { + constructor() { + super(DIFF_TOO_LARGE_MESSAGE); + this.name = "DiffTooLargeError"; + } +} + +export const isDiffTooLarge = (cause: unknown): boolean => + cause instanceof DiffTooLargeError || + (cause instanceof Error && cause.message === DIFF_TOO_LARGE_MESSAGE); + +// Aborts streamed response when body exceeds maxBytes to trigger client-side fetch. +export const cappedFetch = + (fetch: typeof globalThis.fetch, maxBytes: number): typeof globalThis.fetch => + async (input, init) => { + const response = await fetch(input, init); + const body = response.body; + if (!response.ok || !body) return response; + const reader = body.getReader(); + let total = 0; + const stream = new ReadableStream({ + async pull(controller) { + const { done, value } = await reader.read(); + if (done) { + controller.close(); + return; + } + total += value.byteLength; + if (total > maxBytes) { + await reader.cancel().catch(() => undefined); + controller.error(new DiffTooLargeError()); + return; + } + controller.enqueue(value); + }, + cancel: () => { + void reader.cancel().catch(() => undefined); + } + }); + return new Response(stream, { status: response.status, headers: response.headers }); + }; + export const loadDiff = (deps: PullDiffDeps, spec: PullDiffSpec): Promise => { if (spec.kind === "diff") return plainDiff(deps, spec.repo, spec.version); if (!spec.changeId) return interdiff(deps, spec.repo, spec.oldVersion, spec.version); @@ -190,7 +227,7 @@ export const resolveChange = ( // pierre diffs the two sides itself const buildDiff = (diffs: MirrorFileDiff[], contents: ContentsSource): PullDiff => { const stat: DiffStat = { insertions: 0, deletions: 0, files_changed: diffs.length }; - const files = diffs.map((file, index): PullDiffFile => { + const files = diffs.map((file): PullDiffFile => { const fileStat = diffFileStat(file); stat.insertions += fileStat.insertions; stat.deletions += fileStat.deletions; @@ -217,14 +254,10 @@ const buildDiff = (diffs: MirrorFileDiff[], contents: ContentsSource): PullDiff if (tooLarge(file.lhsSrc) || tooLarge(file.rhsSrc)) { return { ...base, note: "This file is too large to display." }; } - if (index >= FILE_LIMIT) { - return { ...base, note: "Not rendered: too many files in this diff." }; - } - return { ...base, sides: { lhs: file.lhsSrc, rhs: file.rhsSrc } }; }); - return { files, stat, truncated: Math.max(diffs.length - FILE_LIMIT, 0), contents }; + return { files, stat, contents }; }; const tooLarge = (src: DiffSrc): boolean => src.size > MAX_INLINE_SIZE; @@ -232,8 +265,14 @@ const tooLarge = (src: DiffSrc): boolean => src.size > MAX_INLINE_SIZE; const renderedLines = (file: MirrorFileDiff): number => file.hunks.reduce((total, hunk) => total + hunk.lines.length + HUNK_CHROME_LINES, 0); -// one side's text: inlined when gitmirror already sent it, otherwise a blob read. -// `null` is a side that could not be read, which is not the same as an empty side +// 429 burst limits, 5xx, and network drops are retryable; 404 is permanent. +const isTransient = (cause: unknown): boolean => { + const status = (cause as { status?: unknown })?.status; + if (typeof status === "number") return status === 429 || status >= 500; + // a network failure carries no status at all + return cause instanceof TypeError; +}; + const sideContents = async ( git: GitTarget, ref: string | undefined, @@ -245,12 +284,18 @@ const sideContents = async ( // the rebased tree an interdiff diffs against is reachable by no ref, so a // missing inline side there can never be recovered if (ref === undefined) return null; - const output = await blob(git, { ref, path: src.path }, MAX_INLINE_SIZE).catch(() => null); + // a rate-limited or briefly failing mirror is worth retrying; a blob that is + // really not there is not, so only the transient shape escapes + const output = await blob(git, { ref, path: src.path }, MAX_INLINE_SIZE).catch( + (cause: unknown) => { + if (isTransient(cause)) throw cause; + return null; + } + ); if (!output || output.encoding !== "utf-8") return null; return output.content ?? ""; }; -// phase two: one file's text, fetched when its card asks for it export const fetchSides = async ( deps: PullDiffDeps, contents: ContentsSource, diff --git a/web/src/lib/assetHeaders.test.ts b/web/src/lib/assetHeaders.test.ts new file mode 100644 index 000000000..3f6fd475d --- /dev/null +++ b/web/src/lib/assetHeaders.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { hardenAssetHeaders, REVALIDATED_IMMUTABLE } from "$lib/assetHeaders"; + +// what @sveltejs/adapter-cloudflare appends to the deployed _headers +const adapterHeaders = ` +# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === +/_app/* + X-Robots-Tag: noindex + Cache-Control: no-cache +/_app/immutable/* + ! Cache-Control + Cache-Control: public, immutable, max-age=31536000 +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === +`; + +describe("hardenAssetHeaders", () => { + it("overrides the adapter's year-long rule from behind it", () => { + const hardened = hardenAssetHeaders(adapterHeaders); + expect(hardened.indexOf(REVALIDATED_IMMUTABLE)).toBeGreaterThan( + hardened.indexOf("public, immutable, max-age=31536000") + ); + }); + + it("leaves the rest of the file alone", () => { + expect(hardenAssetHeaders(adapterHeaders)).toContain("X-Robots-Tag: noindex"); + }); + + it("is idempotent, so a rebuilt file grows no rules", () => { + const once = hardenAssetHeaders(adapterHeaders); + expect(hardenAssetHeaders(once)).toBe(once); + }); +}); diff --git a/web/src/lib/assetHeaders.ts b/web/src/lib/assetHeaders.ts new file mode 100644 index 000000000..3c1d1303e --- /dev/null +++ b/web/src/lib/assetHeaders.ts @@ -0,0 +1,23 @@ +/** cloudflare paints every `_headers` rule onto the response for a matching + * path, including the empty 404 its asset server answers when a hashed file is + * missing. the adapter's autogenerated `immutable, max-age=31536000` therefore + * pins that 404 in the reader's browser for a year: an asset requested a moment + * before its deploy finished landing leaves that browser with an unstyled page + * and a code view that never mounts, and no refresh clears it - the cache + * answers before the network does. + * + * the bytes behind a hashed url are immutable, but its existence is + * deploy-scoped, so the rule that survives is one the browser revalidates: + * a stale entry still paints instantly, and the revalidation behind it turns a + * poisoned 404 back into the asset. */ +export const REVALIDATED_IMMUTABLE = "public, max-age=60, stale-while-revalidate=31536000"; + +const rule = (appDir: string) => + `/${appDir}/immutable/*\n ! Cache-Control\n Cache-Control: ${REVALIDATED_IMMUTABLE}`; + +/** cloudflare applies later rules over earlier ones, so this lands after the + * adapter's autogenerated block rather than replacing it */ +export const hardenAssetHeaders = (headers: string, appDir = "_app"): string => + headers.includes(REVALIDATED_IMMUTABLE) + ? headers + : `${headers.trimEnd()}\n\n# a hashed url's bytes never change, but its existence is deploy-scoped:\n# revalidation is what lets a 404 cached during a deploy heal itself\n${rule(appDir)}\n`; diff --git a/web/src/lib/bones/repo-commitview--full-commit.bones.json b/web/src/lib/bones/repo-commitview--full-commit.bones.json index 9bc5135ce..dd0df8c8a 100644 --- a/web/src/lib/bones/repo-commitview--full-commit.bones.json +++ b/web/src/lib/bones/repo-commitview--full-commit.bones.json @@ -4,7 +4,7 @@ "name": "repo-commitview--full-commit", "viewportWidth": 343, "width": 343, - "height": 1289, + "height": 1355, "bones": [ [4.6647, 0, 90.6706, 437, "0px 4px 4px 4px", true, 15, true], [9.621, 25, 68.9277, 20, 4], @@ -20,98 +20,25 @@ [9.621, 332, 56.9105, 24, 4], [9.621, 364, 56.9105, 24, 4], [9.621, 396, 56.9105, 24, 4], - [5.8309, 468, 4.9016, 18, 4], - [13.0649, 468, 4.9016, 18, 4], - [34.4023, 459, 12.2449, 36, 4], - [48.9796, 459, 12.2449, 36, 4], - [63.5569, 461, 9.9125, 32, 4], - [75.8017, 460, 19.5335, 34, 4, true, 15, true], - [75.8017, 461, 9.9125, 32, "4px 0px 0px 4px"], - [85.4227, 461, 9.9125, 32, "0px 4px 4px 0px"], - [4.6647, 501, 90.6706, 652, 0, true, 0, true], - [4.6647, 501, 90.6706, 42, "4px 4px 0px 0px", true, 15, false], - [7.8717, 515, 4.0816, 14, 0], - [14.2857, 510, 13.0649, 24, 4], - [29.6829, 514, 23.9568, 17, 4], - [4.6647, 543, 90.6706, 610, "0px 0px 4px 4px", true, 14, false], - [12.3041, 544, 2.4508, 18, 4], - [12.3041, 565, 2.4508, 18, 4], - [12.3041, 586, 2.4508, 18, 4], - [12.3041, 607, 2.4508, 18, 4], - [12.3041, 628, 2.4508, 18, 4], - [12.3041, 649, 2.4508, 18, 4], - [12.3041, 670, 2.4508, 18, 4], - [12.3041, 691, 2.4508, 18, 4], - [4.9563, 711, 12.2449, 21, 0, true, 0, true], - [12.3041, 712, 2.4508, 18, 4], - [4.9563, 732, 12.2449, 21, 0, true, 0, true], - [9.8533, 733, 4.9016, 18, 4], - [4.9563, 753, 12.2449, 21, 0, true, 0, true], - [12.3041, 754, 2.4508, 18, 4], - [4.9563, 774, 12.2449, 21, 0, true, 0, true], - [9.8533, 775, 4.9016, 18, 4], - [9.8533, 796, 4.9016, 18, 4], - [4.9563, 816, 12.2449, 21, 0, true, 0, true], - [9.8533, 817, 4.9016, 18, 4], - [9.8533, 838, 4.9016, 18, 4], - [9.8533, 859, 4.9016, 18, 4], - [9.8533, 880, 4.9016, 18, 4], - [4.9563, 900, 12.2449, 21, 0, true, 0, true], - [9.8533, 901, 4.9016, 18, 4], - [4.9563, 921, 12.2449, 21, 0, true, 0, true], - [9.8533, 922, 4.9016, 18, 4], - [4.9563, 942, 12.2449, 21, 0, true, 0, true], - [9.8533, 943, 4.9016, 18, 4], - [9.8533, 964, 4.9016, 18, 4], - [9.8533, 985, 4.9016, 18, 4], - [9.8533, 1006, 4.9016, 18, 4], - [9.8533, 1027, 4.9016, 18, 4], - [9.8533, 1048, 4.9016, 18, 4], - [9.8533, 1069, 4.9016, 18, 4], - [9.8533, 1090, 4.9016, 18, 4], - [4.9563, 1110, 12.2449, 21, 0, true, 0, true], - [9.8533, 1111, 4.9016, 18, 4], - [9.8533, 1132, 4.9016, 18, 4], - [22.0982, 544, 63.6798, 18, 4], - [22.0982, 565, 51.4395, 18, 4], - [22.0982, 586, 51.4395, 18, 4], - [22.0982, 607, 41.6454, 18, 4], - [22.0982, 628, 41.6454, 18, 4], - [22.0982, 649, 2.4508, 18, 4], - [22.0982, 691, 71.0277, 18, 4], - [22.0982, 712, 53.8903, 18, 4], - [22.0982, 733, 53.8903, 18, 4], - [22.0982, 754, 73.4785, 18, 4], - [22.0982, 775, 73.4831, 18, 4], - [22.0982, 796, 68.5815, 18, 4], - [22.0982, 817, 77.9018, 18, 4], - [22.0982, 838, 4.9016, 18, 4], - [22.0982, 880, 56.3411, 18, 4], - [22.0982, 901, 44.1008, 18, 4], - [22.0982, 922, 71.0368, 18, 4], - [22.0982, 943, 77.9018, 18, 4], - [22.0982, 964, 77.9018, 18, 4], - [22.0982, 985, 75.9293, 18, 4], - [22.0982, 1006, 4.9016, 18, 4], - [22.0982, 1048, 53.8903, 18, 4], - [22.0982, 1069, 24.4989, 18, 4], - [22.0982, 1090, 22.0481, 18, 4], - [22.0982, 1111, 31.8468, 18, 4], - [22.0982, 1132, 4.9016, 18, 4], - [4.6647, 1169, 90.6706, 120, 0, true, 0, true], - [4.6647, 1169, 90.6706, 39, "4px 4px 0px 0px", true, 15, false], - [7.8717, 1182, 4.0816, 14, 0], - [14.2857, 1180, 30.8765, 17, 4], - [4.6647, 1208, 90.6706, 81, "0px 0px 4px 4px", true, 14, false], - [9.621, 1227, 65.2788, 17, 4], - [9.621, 1251, 19.6884, 17, 4] + [4.6647, 453, 90.6706, 902, 4, true, 15, true], + [7.2886, 464, 12.2449, 36, 4], + [11.3703, 525, 4.6647, 16, 0], + [18.3673, 524, 5.9585, 17, 4], + [13.7026, 553, 82.2157, 33, 0, true, 8, false], + [18.6589, 562, 4.6647, 16, 0], + [25.656, 561, 16.5315, 17, 4], + [11.3703, 599, 4.6647, 16, 0], + [18.3673, 598, 12.4317, 17, 4], + [13.7026, 627, 82.2157, 33, 0, true, 8, false], + [18.6589, 636, 4.6647, 16, 0], + [25.656, 635, 16.9734, 17, 4] ] }, "768": { "name": "repo-commitview--full-commit", "viewportWidth": 736, "width": 736, - "height": 1221, + "height": 1311, "bones": [ [2.1739, 0, 95.6522, 393, "0px 4px 4px 4px", true, 15, true], [4.4837, 25, 32.1225, 20, 4], @@ -121,116 +48,70 @@ [4.4837, 160, 25.9744, 24, 4], [4.4837, 192, 24.8939, 24, 4], [4.4837, 224, 25.5774, 24, 4], - [4.4837, 256, 57.6087, 24, 4], + [4.4837, 256, 56.3753, 24, 4], [4.4837, 288, 63.0435, 24, 4], [4.4837, 320, 63.0435, 24, 4], [4.4837, 352, 31.0887, 24, 4], - [2.1739, 417, 4.6196, 32, 4], - [8.4239, 424, 2.2843, 18, 4], - [11.7952, 424, 2.2843, 18, 4], - [15.7099, 425, 11.8907, 15, 4], - [30.2522, 415, 21.4186, 36, 4], - [52.7577, 415, 19.3126, 36, 4], - [73.1573, 417, 14.4786, 32, 4], - [88.7228, 416, 9.1033, 34, 4, true, 15, true], - [88.7228, 417, 4.6196, 32, "4px 0px 0px 4px"], - [93.2065, 417, 4.6196, 32, "0px 4px 4px 0px"], - [2.1739, 457, 26.5243, 764, 4, true, 15, true], - [7.4728, 482, 2.1739, 16, 0], - [10.7337, 481, 2.7768, 17, 4], - [8.5598, 512, 14.5678, 36, 0, true, 8, false], - [10.8696, 522, 2.1739, 16, 0], - [14.1304, 521, 7.7042, 17, 4], - [7.4728, 562, 2.1739, 16, 0], - [10.7337, 561, 5.7936, 17, 4], - [8.5598, 592, 14.5678, 36, 0, true, 8, false], - [10.8696, 602, 2.1739, 16, 0], - [14.1304, 601, 7.9102, 17, 4], - [30.8721, 457, 66.954, 652, 0, true, 0, true], - [30.8721, 457, 66.954, 42, "4px 4px 0px 0px", true, 15, false], - [32.3667, 471, 1.9022, 14, 0], - [35.3558, 466, 6.0887, 24, 4], - [42.5314, 470, 11.1647, 17, 4], - [85.1244, 462, 12.0223, 32, 4], - [30.8721, 499, 66.954, 610, "0px 0px 4px 4px", true, 14, false], - [34.4323, 500, 1.1422, 18, 4], - [34.4323, 521, 1.1422, 18, 4], - [34.4323, 542, 1.1422, 18, 4], - [34.4323, 563, 1.1422, 18, 4], - [34.4323, 584, 1.1422, 18, 4], - [34.4323, 605, 1.1422, 18, 4], - [34.4323, 626, 1.1422, 18, 4], - [34.4323, 647, 1.1422, 18, 4], - [31.008, 667, 5.7065, 21, 0, true, 0, true], - [34.4323, 668, 1.1422, 18, 4], - [31.008, 688, 5.7065, 21, 0, true, 0, true], - [33.2902, 689, 2.2843, 18, 4], - [31.008, 709, 5.7065, 21, 0, true, 0, true], - [34.4323, 710, 1.1422, 18, 4], - [31.008, 730, 5.7065, 21, 0, true, 0, true], - [33.2902, 731, 2.2843, 18, 4], - [33.2902, 752, 2.2843, 18, 4], - [31.008, 772, 5.7065, 21, 0, true, 0, true], - [33.2902, 773, 2.2843, 18, 4], - [33.2902, 794, 2.2843, 18, 4], - [33.2902, 815, 2.2843, 18, 4], - [33.2902, 836, 2.2843, 18, 4], - [31.008, 856, 5.7065, 21, 0, true, 0, true], - [33.2902, 857, 2.2843, 18, 4], - [31.008, 877, 5.7065, 21, 0, true, 0, true], - [33.2902, 878, 2.2843, 18, 4], - [31.008, 898, 5.7065, 21, 0, true, 0, true], - [33.2902, 899, 2.2843, 18, 4], - [33.2902, 920, 2.2843, 18, 4], - [33.2902, 941, 2.2843, 18, 4], - [33.2902, 962, 2.2843, 18, 4], - [33.2902, 983, 2.2843, 18, 4], - [33.2902, 1004, 2.2843, 18, 4], - [33.2902, 1025, 2.2843, 18, 4], - [33.2902, 1046, 2.2843, 18, 4], - [31.008, 1066, 5.7065, 21, 0, true, 0, true], - [33.2902, 1067, 2.2843, 18, 4], - [33.2902, 1088, 2.2843, 18, 4], - [38.9967, 500, 29.6769, 18, 4], - [38.9967, 521, 23.9725, 18, 4], - [38.9967, 542, 23.9725, 18, 4], - [38.9967, 563, 19.4081, 18, 4], - [38.9967, 584, 19.4081, 18, 4], - [38.9967, 605, 1.1422, 18, 4], - [38.9967, 647, 37.6698, 18, 4], - [38.9967, 668, 25.1146, 18, 4], - [38.9967, 689, 25.1146, 18, 4], - [38.9967, 710, 36.5277, 18, 4], - [38.9967, 731, 35.3877, 18, 4], - [38.9967, 752, 31.9612, 18, 4], - [38.9967, 773, 37.6677, 18, 4], - [38.9967, 794, 2.2843, 18, 4], - [38.9967, 836, 26.2568, 18, 4], - [38.9967, 857, 20.5524, 18, 4], - [38.9967, 878, 33.1055, 18, 4], - [38.9967, 899, 37.6698, 18, 4], - [38.9967, 920, 37.6677, 18, 4], - [38.9967, 941, 35.3855, 18, 4], - [38.9967, 962, 2.2843, 18, 4], - [38.9967, 1004, 25.1146, 18, 4], - [38.9967, 1025, 11.4173, 18, 4], - [38.9967, 1046, 10.2751, 18, 4], - [38.9967, 1067, 14.8416, 18, 4], - [38.9967, 1088, 2.2843, 18, 4], - [30.8721, 1125, 66.954, 96, 0, true, 0, true], - [30.8721, 1125, 66.954, 39, "4px 4px 0px 0px", true, 15, false], - [32.3667, 1138, 1.9022, 14, 0], - [35.3558, 1136, 14.3894, 17, 4], - [85.1244, 1129, 12.0223, 32, 4], - [30.8721, 1164, 66.954, 57, "0px 0px 4px 4px", true, 14, false], - [44.2956, 1183, 40.107, 17, 4] + [2.1739, 409, 95.6522, 902, 4, true, 15, true], + [3.3967, 420, 5.7065, 36, 4], + [5.2989, 481, 2.1739, 16, 0], + [8.5598, 480, 2.7768, 17, 4], + [6.3859, 509, 38.3152, 33, 0, true, 8, false], + [8.6957, 518, 2.1739, 16, 0], + [11.9565, 517, 7.7042, 17, 4], + [5.2989, 555, 2.1739, 16, 0], + [8.5598, 554, 5.7936, 17, 4], + [6.3859, 583, 38.3152, 33, 0, true, 8, false], + [8.6957, 592, 2.1739, 16, 0], + [11.9565, 591, 7.9102, 17, 4], + [47.962, 410, 0.1359, 900, 4], + [50.2717, 441, 2.2843, 18, 4], + [53.643, 441, 2.2843, 18, 4], + [58.1012, 420, 1.2334, 17, 4], + [58.1012, 441, 7.8443, 17, 4], + [58.1012, 462, 3.7746, 17, 4], + [67.0325, 421, 13.3364, 58, 4], + [81.4559, 421, 18.5441, 58, 4], + [49.1848, 489, 47.4185, 316, 4, true, 15, false], + [52.745, 532, 1.1422, 18, 4], + [52.745, 553, 1.1422, 18, 4], + [52.745, 574, 1.1422, 18, 4], + [49.3207, 594, 5.7065, 21, 0, true, 0, true], + [52.745, 595, 1.1422, 18, 4], + [52.745, 616, 1.1422, 18, 4], + [52.745, 637, 1.1422, 18, 4], + [52.745, 658, 1.1422, 18, 4], + [49.3207, 678, 5.7065, 21, 0, true, 0, true], + [52.745, 679, 1.1422, 18, 4], + [49.3207, 699, 5.7065, 21, 0, true, 0, true], + [52.745, 700, 1.1422, 18, 4], + [49.3207, 720, 5.7065, 21, 0, true, 0, true], + [52.745, 721, 1.1422, 18, 4], + [49.3207, 741, 5.7065, 21, 0, true, 0, true], + [52.745, 742, 1.1422, 18, 4], + [49.3207, 762, 5.7065, 21, 0, true, 0, true], + [51.6028, 763, 2.2843, 18, 4], + [51.6028, 784, 2.2843, 18, 4], + [57.3094, 532, 29.6769, 18, 4], + [57.3094, 553, 23.9725, 18, 4], + [57.3094, 574, 23.9725, 18, 4], + [57.3094, 595, 19.4081, 18, 4], + [57.3094, 616, 1.1422, 18, 4], + [57.3094, 658, 37.6698, 18, 4], + [57.3094, 679, 25.1146, 18, 4], + [57.3094, 700, 23.9725, 18, 4], + [57.3094, 721, 36.5277, 18, 4], + [57.3094, 742, 35.3855, 18, 4], + [57.3094, 763, 36.5256, 18, 4], + [57.3094, 784, 2.2843, 18, 4], + [49.1848, 813, 47.4185, 59, 4, true, 15, false] ] }, "1024": { "name": "repo-commitview--full-commit", "viewportWidth": 992, "width": 992, - "height": 1221, + "height": 1311, "bones": [ [1.6129, 0, 96.7742, 393, "0px 4px 4px 4px", true, 15, true], [3.3266, 25, 23.8329, 20, 4], @@ -240,116 +121,72 @@ [3.3266, 160, 19.2714, 24, 4], [3.3266, 192, 18.4696, 24, 4], [3.3266, 224, 18.9768, 24, 4], - [3.3266, 256, 42.7419, 24, 4], + [3.3266, 256, 41.8268, 24, 4], [3.3266, 288, 46.7742, 24, 4], [3.3266, 320, 46.7742, 24, 4], [3.3266, 352, 23.0658, 24, 4], - [1.6129, 417, 3.4274, 32, 4], - [6.25, 424, 1.6948, 18, 4], - [8.7513, 424, 1.6948, 18, 4], - [11.6557, 425, 8.8221, 15, 4], - [48.2516, 415, 15.8912, 36, 4], - [64.9493, 415, 14.3287, 36, 4], - [80.0844, 417, 10.7422, 32, 4], - [91.6331, 416, 6.754, 34, 4, true, 15, true], - [91.6331, 417, 3.4274, 32, "4px 0px 0px 4px"], - [94.9597, 417, 3.4274, 32, "0px 4px 4px 0px"], - [1.6129, 457, 19.6793, 764, 4, true, 15, true], - [5.5444, 482, 1.6129, 16, 0], - [7.9637, 481, 2.0602, 17, 4], - [6.3508, 512, 10.8083, 36, 0, true, 8, false], - [8.0645, 522, 1.6129, 16, 0], - [10.4839, 521, 5.716, 17, 4], - [5.5444, 562, 1.6129, 16, 0], - [7.9637, 561, 4.2985, 17, 4], - [6.3508, 592, 10.8083, 36, 0, true, 8, false], - [8.0645, 602, 1.6129, 16, 0], - [10.4839, 601, 5.8688, 17, 4], - [22.9051, 457, 75.482, 652, 0, true, 0, true], - [22.9051, 457, 75.482, 42, "4px 4px 0px 0px", true, 15, false], - [24.014, 471, 1.4113, 14, 0], - [26.2317, 466, 4.5174, 24, 4], - [31.5556, 470, 8.2835, 17, 4], - [88.9633, 462, 8.9198, 32, 4], - [22.9051, 499, 75.482, 610, "0px 0px 4px 4px", true, 14, false], - [25.5466, 500, 0.8474, 18, 4], - [25.5466, 521, 0.8474, 18, 4], - [25.5466, 542, 0.8474, 18, 4], - [25.5466, 563, 0.8474, 18, 4], - [25.5466, 584, 0.8474, 18, 4], - [25.5466, 605, 0.8474, 18, 4], - [25.5466, 626, 0.8474, 18, 4], - [25.5466, 647, 0.8474, 18, 4], - [23.0059, 667, 4.2339, 21, 0, true, 0, true], - [25.5466, 668, 0.8474, 18, 4], - [23.0059, 688, 4.2339, 21, 0, true, 0, true], - [24.6992, 689, 1.6948, 18, 4], - [23.0059, 709, 4.2339, 21, 0, true, 0, true], - [25.5466, 710, 0.8474, 18, 4], - [23.0059, 730, 4.2339, 21, 0, true, 0, true], - [24.6992, 731, 1.6948, 18, 4], - [24.6992, 752, 1.6948, 18, 4], - [23.0059, 772, 4.2339, 21, 0, true, 0, true], - [24.6992, 773, 1.6948, 18, 4], - [24.6992, 794, 1.6948, 18, 4], - [24.6992, 815, 1.6948, 18, 4], - [24.6992, 836, 1.6948, 18, 4], - [23.0059, 856, 4.2339, 21, 0, true, 0, true], - [24.6992, 857, 1.6948, 18, 4], - [23.0059, 877, 4.2339, 21, 0, true, 0, true], - [24.6992, 878, 1.6948, 18, 4], - [23.0059, 898, 4.2339, 21, 0, true, 0, true], - [24.6992, 899, 1.6948, 18, 4], - [24.6992, 920, 1.6948, 18, 4], - [24.6992, 941, 1.6948, 18, 4], - [24.6992, 962, 1.6948, 18, 4], - [24.6992, 983, 1.6948, 18, 4], - [24.6992, 1004, 1.6948, 18, 4], - [24.6992, 1025, 1.6948, 18, 4], - [24.6992, 1046, 1.6948, 18, 4], - [23.0059, 1066, 4.2339, 21, 0, true, 0, true], - [24.6992, 1067, 1.6948, 18, 4], - [24.6992, 1088, 1.6948, 18, 4], - [28.933, 500, 22.0183, 18, 4], - [28.933, 521, 17.786, 18, 4], - [28.933, 542, 17.786, 18, 4], - [28.933, 563, 14.3996, 18, 4], - [28.933, 584, 14.3996, 18, 4], - [28.933, 605, 0.8474, 18, 4], - [28.933, 647, 27.9486, 18, 4], - [28.933, 668, 18.6334, 18, 4], - [28.933, 689, 18.6334, 18, 4], - [28.933, 710, 27.1012, 18, 4], - [28.933, 731, 26.2554, 18, 4], - [28.933, 752, 23.7131, 18, 4], - [28.933, 773, 27.947, 18, 4], - [28.933, 794, 1.6948, 18, 4], - [28.933, 836, 19.4808, 18, 4], - [28.933, 857, 15.2486, 18, 4], - [28.933, 878, 24.5621, 18, 4], - [28.933, 899, 27.9486, 18, 4], - [28.933, 920, 27.947, 18, 4], - [28.933, 941, 26.2538, 18, 4], - [28.933, 962, 1.6948, 18, 4], - [28.933, 1004, 18.6334, 18, 4], - [28.933, 1025, 8.4709, 18, 4], - [28.933, 1046, 7.6235, 18, 4], - [28.933, 1067, 11.0115, 18, 4], - [28.933, 1088, 1.6948, 18, 4], - [22.9051, 1125, 75.482, 96, 0, true, 0, true], - [22.9051, 1125, 75.482, 39, "4px 4px 0px 0px", true, 15, false], - [24.014, 1138, 1.4113, 14, 0], - [26.2317, 1136, 10.676, 17, 4], - [88.9633, 1129, 8.9198, 32, 4], - [22.9051, 1164, 75.482, 57, "0px 0px 4px 4px", true, 14, false], - [45.7677, 1183, 29.7568, 17, 4] + [1.6129, 409, 96.7742, 902, 4, true, 15, true], + [2.5202, 420, 4.2339, 36, 4], + [3.9315, 481, 1.6129, 16, 0], + [6.3508, 480, 2.0602, 17, 4], + [4.7379, 509, 28.4274, 33, 0, true, 8, false], + [6.4516, 518, 1.6129, 16, 0], + [8.871, 517, 5.716, 17, 4], + [3.9315, 555, 1.6129, 16, 0], + [6.3508, 554, 4.2985, 17, 4], + [4.7379, 583, 28.4274, 33, 0, true, 8, false], + [6.4516, 592, 1.6129, 16, 0], + [8.871, 591, 5.8688, 17, 4], + [35.5847, 410, 0.1008, 900, 4], + [37.2984, 438, 1.6948, 18, 4], + [39.7996, 438, 1.6948, 18, 4], + [43.1074, 428, 7.1147, 17, 4], + [43.1074, 449, 2.8005, 17, 4], + [53.1423, 418, 10.3295, 58, 4], + [64.2783, 418, 15.9227, 58, 4], + [81.0074, 418, 16.4724, 58, 4, true, 15, true], + [81.0074, 419, 9.1041, 56, "4px 0px 0px 4px"], + [90.0107, 419, 7.4691, 56, "0px 4px 4px 0px"], + [36.4919, 484, 60.9879, 316, 4, true, 15, false], + [39.1334, 527, 0.8474, 18, 4], + [39.1334, 548, 0.8474, 18, 4], + [39.1334, 569, 0.8474, 18, 4], + [36.5927, 589, 4.2339, 21, 0, true, 0, true], + [39.1334, 590, 0.8474, 18, 4], + [39.1334, 611, 0.8474, 18, 4], + [39.1334, 632, 0.8474, 18, 4], + [39.1334, 653, 0.8474, 18, 4], + [36.5927, 673, 4.2339, 21, 0, true, 0, true], + [39.1334, 674, 0.8474, 18, 4], + [36.5927, 694, 4.2339, 21, 0, true, 0, true], + [39.1334, 695, 0.8474, 18, 4], + [36.5927, 715, 4.2339, 21, 0, true, 0, true], + [39.1334, 716, 0.8474, 18, 4], + [36.5927, 736, 4.2339, 21, 0, true, 0, true], + [39.1334, 737, 0.8474, 18, 4], + [36.5927, 757, 4.2339, 21, 0, true, 0, true], + [38.286, 758, 1.6948, 18, 4], + [38.286, 779, 1.6948, 18, 4], + [42.5198, 527, 22.0183, 18, 4], + [42.5198, 548, 17.786, 18, 4], + [42.5198, 569, 17.786, 18, 4], + [42.5198, 590, 14.3996, 18, 4], + [42.5198, 611, 0.8474, 18, 4], + [42.5198, 653, 27.9486, 18, 4], + [42.5198, 674, 18.6334, 18, 4], + [42.5198, 695, 17.786, 18, 4], + [42.5198, 716, 27.1012, 18, 4], + [42.5198, 737, 26.2538, 18, 4], + [42.5198, 758, 27.0996, 18, 4], + [42.5198, 779, 1.6948, 18, 4], + [36.4919, 808, 60.9879, 40, 4, true, 15, false] ] }, "1440": { "name": "repo-commitview--full-commit", "viewportWidth": 1408, "width": 1408, - "height": 1221, + "height": 1311, "bones": [ [1.1364, 0, 97.7273, 393, "0px 4px 4px 4px", true, 15, true], [2.3438, 25, 16.7913, 20, 4], @@ -359,111 +196,66 @@ [2.3438, 160, 13.5775, 24, 4], [2.3438, 192, 13.0127, 24, 4], [2.3438, 224, 13.37, 24, 4], - [2.3438, 256, 30.1136, 24, 4], + [2.3438, 256, 29.4689, 24, 4], [2.3438, 288, 32.9545, 24, 4], [2.3438, 320, 32.9545, 24, 4], [2.3438, 352, 16.2509, 24, 4], - [1.1364, 417, 2.4148, 32, 4], - [4.4034, 424, 1.1941, 18, 4], - [6.1657, 424, 1.1941, 18, 4], - [8.212, 425, 6.2156, 15, 4], - [63.5409, 415, 11.1961, 36, 4], - [75.3052, 415, 10.0952, 36, 4], - [85.9686, 417, 7.5684, 32, 4], - [94.1051, 416, 4.7585, 34, 4, true, 15, true], - [94.1051, 417, 2.4148, 32, "4px 0px 0px 4px"], - [96.4489, 417, 2.4148, 32, "0px 4px 4px 0px"], - [1.1364, 457, 13.865, 764, 4, true, 15, true], - [3.9063, 482, 1.1364, 16, 0], - [5.6108, 481, 1.4515, 17, 4], - [4.4744, 512, 7.615, 36, 0, true, 8, false], - [5.6818, 522, 1.1364, 16, 0], - [7.3864, 521, 4.0272, 17, 4], - [3.9063, 562, 1.1364, 16, 0], - [5.6108, 561, 3.0285, 17, 4], - [4.4744, 592, 7.615, 36, 0, true, 8, false], - [5.6818, 602, 1.1364, 16, 0], - [7.3864, 601, 4.1349, 17, 4], - [16.1377, 457, 82.7259, 652, 0, true, 0, true], - [16.1377, 457, 82.7259, 42, "4px 4px 0px 0px", true, 15, false], - [16.9189, 471, 0.9943, 14, 0], - [18.4814, 466, 3.1827, 24, 4], - [22.2323, 470, 5.8361, 17, 4], - [92.2241, 462, 6.2844, 32, 4], - [16.1377, 499, 82.7259, 610, "0px 0px 4px 4px", true, 14, false], - [17.9987, 500, 0.597, 18, 4], - [17.9987, 521, 0.597, 18, 4], - [17.9987, 542, 0.597, 18, 4], - [17.9987, 563, 0.597, 18, 4], - [17.9987, 584, 0.597, 18, 4], - [17.9987, 605, 0.597, 18, 4], - [17.9987, 626, 0.597, 18, 4], - [17.9987, 647, 0.597, 18, 4], - [16.2087, 667, 2.983, 21, 0, true, 0, true], - [17.9987, 668, 0.597, 18, 4], - [16.2087, 688, 2.983, 21, 0, true, 0, true], - [17.4017, 689, 1.1941, 18, 4], - [16.2087, 709, 2.983, 21, 0, true, 0, true], - [17.9987, 710, 0.597, 18, 4], - [16.2087, 730, 2.983, 21, 0, true, 0, true], - [17.4017, 731, 1.1941, 18, 4], - [17.4017, 752, 1.1941, 18, 4], - [16.2087, 772, 2.983, 21, 0, true, 0, true], - [17.4017, 773, 1.1941, 18, 4], - [17.4017, 794, 1.1941, 18, 4], - [17.4017, 815, 1.1941, 18, 4], - [17.4017, 836, 1.1941, 18, 4], - [16.2087, 856, 2.983, 21, 0, true, 0, true], - [17.4017, 857, 1.1941, 18, 4], - [16.2087, 877, 2.983, 21, 0, true, 0, true], - [17.4017, 878, 1.1941, 18, 4], - [16.2087, 898, 2.983, 21, 0, true, 0, true], - [17.4017, 899, 1.1941, 18, 4], - [17.4017, 920, 1.1941, 18, 4], - [17.4017, 941, 1.1941, 18, 4], - [17.4017, 962, 1.1941, 18, 4], - [17.4017, 983, 1.1941, 18, 4], - [17.4017, 1004, 1.1941, 18, 4], - [17.4017, 1025, 1.1941, 18, 4], - [17.4017, 1046, 1.1941, 18, 4], - [16.2087, 1066, 2.983, 21, 0, true, 0, true], - [17.4017, 1067, 1.1941, 18, 4], - [17.4017, 1088, 1.1941, 18, 4], - [20.3846, 500, 15.5129, 18, 4], - [20.3846, 521, 12.5311, 18, 4], - [20.3846, 542, 12.5311, 18, 4], - [20.3846, 563, 10.1452, 18, 4], - [20.3846, 584, 10.1452, 18, 4], - [20.3846, 605, 0.597, 18, 4], - [20.3846, 647, 19.6911, 18, 4], - [20.3846, 668, 13.1281, 18, 4], - [20.3846, 689, 13.1281, 18, 4], - [20.3846, 710, 19.094, 18, 4], - [20.3846, 731, 18.4981, 18, 4], - [20.3846, 752, 16.707, 18, 4], - [20.3846, 773, 19.6899, 18, 4], - [20.3846, 794, 1.1941, 18, 4], - [20.3846, 836, 13.7251, 18, 4], - [20.3846, 857, 10.7433, 18, 4], - [20.3846, 878, 17.3051, 18, 4], - [20.3846, 899, 19.6911, 18, 4], - [20.3846, 920, 19.6899, 18, 4], - [20.3846, 941, 18.497, 18, 4], - [20.3846, 962, 1.1941, 18, 4], - [20.3846, 1004, 13.1281, 18, 4], - [20.3846, 1025, 5.9681, 18, 4], - [20.3846, 1046, 5.3711, 18, 4], - [20.3846, 1067, 7.7581, 18, 4], - [20.3846, 1088, 1.1941, 18, 4], - [16.1377, 1125, 82.7259, 96, 0, true, 0, true], - [16.1377, 1125, 82.7259, 39, "4px 4px 0px 0px", true, 15, false], - [16.9189, 1138, 0.9943, 14, 0], - [18.4814, 1136, 7.5218, 17, 4], - [92.2241, 1129, 6.2844, 32, 4], - [16.1377, 1164, 82.7259, 57, "0px 0px 4px 4px", true, 14, false], - [47.0182, 1183, 20.965, 17, 4] + [1.1364, 409, 97.7273, 902, 4, true, 15, true], + [1.7756, 420, 2.983, 36, 4], + [2.7699, 481, 1.1364, 16, 0], + [4.4744, 480, 1.4515, 17, 4], + [3.3381, 509, 20.0284, 33, 0, true, 8, false], + [4.5455, 518, 1.1364, 16, 0], + [6.25, 517, 4.0272, 17, 4], + [2.7699, 555, 1.1364, 16, 0], + [4.4744, 554, 3.0285, 17, 4], + [3.3381, 583, 20.0284, 33, 0, true, 8, false], + [4.5455, 592, 1.1364, 16, 0], + [6.25, 591, 4.1349, 17, 4], + [25.071, 410, 0.071, 900, 4], + [26.2784, 429, 1.1941, 18, 4], + [28.0407, 429, 1.1941, 18, 4], + [30.3711, 430, 7.2521, 17, 4], + [64.8327, 419, 7.6938, 38, 4], + [73.0946, 419, 12.0772, 38, 4], + [85.74, 419, 12.4845, 38, 4, true, 15, true], + [85.74, 420, 6.9169, 36, "4px 0px 0px 4px"], + [92.5859, 420, 5.6385, 36, "0px 4px 4px 0px"], + [25.7102, 466, 72.5142, 316, 4, true, 15, false], + [27.5712, 509, 0.597, 18, 4], + [27.5712, 530, 0.597, 18, 4], + [27.5712, 551, 0.597, 18, 4], + [25.7813, 571, 2.983, 21, 0, true, 0, true], + [27.5712, 572, 0.597, 18, 4], + [27.5712, 593, 0.597, 18, 4], + [27.5712, 614, 0.597, 18, 4], + [27.5712, 635, 0.597, 18, 4], + [25.7813, 655, 2.983, 21, 0, true, 0, true], + [27.5712, 656, 0.597, 18, 4], + [25.7813, 676, 2.983, 21, 0, true, 0, true], + [27.5712, 677, 0.597, 18, 4], + [25.7813, 697, 2.983, 21, 0, true, 0, true], + [27.5712, 698, 0.597, 18, 4], + [25.7813, 718, 2.983, 21, 0, true, 0, true], + [27.5712, 719, 0.597, 18, 4], + [25.7813, 739, 2.983, 21, 0, true, 0, true], + [26.9742, 740, 1.1941, 18, 4], + [26.9742, 761, 1.1941, 18, 4], + [29.9572, 509, 15.5129, 18, 4], + [29.9572, 530, 12.5311, 18, 4], + [29.9572, 551, 12.5311, 18, 4], + [29.9572, 572, 10.1452, 18, 4], + [29.9572, 593, 0.597, 18, 4], + [29.9572, 635, 19.6911, 18, 4], + [29.9572, 656, 13.1281, 18, 4], + [29.9572, 677, 12.5311, 18, 4], + [29.9572, 698, 19.094, 18, 4], + [29.9572, 719, 18.497, 18, 4], + [29.9572, 740, 19.0929, 18, 4], + [29.9572, 761, 1.1941, 18, 4], + [25.7102, 790, 72.5142, 40, 4, true, 15, false] ] } }, - "_hash": "f458037817150511d0a7638b3c3fde5d" + "_hash": "86430672dab63591b6057b65de6ada57" } diff --git a/web/src/lib/bones/repo-difffilecard--skeleton-fixture.bones.json b/web/src/lib/bones/repo-difffilecard--skeleton-fixture.bones.json deleted file mode 100644 index e8e84cbf8..000000000 --- a/web/src/lib/bones/repo-difffilecard--skeleton-fixture.bones.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "breakpoints": { - "375": { - "name": "repo-difffilecard--skeleton-fixture", - "viewportWidth": 343, - "width": 343, - "height": 421, - "bones": [ - [0, 0, 100, 421, 0, true, 0, true], - [0, 32, 100, 42, "4px 4px 0px 0px", true, 15, false], - [3.207, 46, 4.0816, 14, 0], - [9.621, 41, 13.0649, 24, 4], - [25.0182, 45, 60.1266, 17, 4], - [0, 42, 100, 379, "0px 0px 4px 4px", true, 14, false], - [0.2915, 84, 12.2449, 21, 0, true, 0, true], - [7.6394, 85, 2.4508, 18, 4], - [0.2915, 105, 12.2449, 21, 0, true, 0, true], - [7.6394, 106, 2.4508, 18, 4], - [7.6394, 127, 2.4508, 18, 4], - [7.6394, 148, 2.4508, 18, 4], - [0.2915, 168, 12.2449, 21, 0, true, 0, true], - [7.6394, 169, 2.4508, 18, 4], - [0.2915, 189, 12.2449, 21, 0, true, 0, true], - [7.6394, 190, 2.4508, 18, 4], - [7.6394, 211, 2.4508, 18, 4], - [7.6394, 232, 2.4508, 18, 4], - [0.2915, 252, 12.2449, 21, 0, true, 0, true], - [5.1886, 253, 4.9016, 18, 4], - [7.6394, 274, 2.4508, 18, 4], - [7.6394, 295, 2.4508, 18, 4], - [0.2915, 315, 12.2449, 21, 0, true, 0, true], - [5.1886, 316, 4.9016, 18, 4], - [0.2915, 336, 12.2449, 21, 0, true, 0, true], - [5.1886, 337, 4.9016, 18, 4], - [0.2915, 357, 12.2449, 21, 0, true, 0, true], - [5.1886, 358, 4.9016, 18, 4], - [0.2915, 378, 12.2449, 21, 0, true, 0, true], - [5.1886, 379, 4.9016, 18, 4], - [0.2915, 399, 12.2449, 21, 0, true, 0, true], - [5.1886, 400, 4.9016, 18, 4], - [17.4335, 85, 46.5425, 18, 4], - [17.4335, 106, 51.4395, 18, 4], - [17.4335, 127, 82.5665, 18, 4], - [17.4335, 169, 82.5665, 18, 4], - [17.4335, 190, 82.5665, 18, 4], - [17.4335, 232, 61.2336, 18, 4], - [17.4335, 253, 80.8355, 18, 4], - [17.4335, 274, 22.0481, 18, 4], - [17.4335, 316, 22.0481, 18, 4], - [17.4335, 337, 56.3411, 18, 4], - [17.4335, 358, 75.9293, 18, 4], - [17.4335, 379, 29.4005, 18, 4], - [17.4335, 400, 31.8468, 18, 4] - ] - }, - "768": { - "name": "repo-difffilecard--skeleton-fixture", - "viewportWidth": 736, - "width": 736, - "height": 421, - "bones": [ - [0, 0, 100, 421, 0, true, 0, true], - [0, 32, 100, 42, "4px 4px 0px 0px", true, 15, false], - [1.4946, 46, 1.9022, 14, 0], - [4.4837, 41, 6.0887, 24, 4], - [11.6593, 45, 28.021, 17, 4], - [0, 42, 100, 379, "0px 0px 4px 4px", true, 14, false], - [0.1359, 84, 5.7065, 21, 0, true, 0, true], - [3.5602, 85, 1.1422, 18, 4], - [0.1359, 105, 5.7065, 21, 0, true, 0, true], - [3.5602, 106, 1.1422, 18, 4], - [3.5602, 127, 1.1422, 18, 4], - [3.5602, 148, 1.1422, 18, 4], - [0.1359, 168, 5.7065, 21, 0, true, 0, true], - [3.5602, 169, 1.1422, 18, 4], - [0.1359, 189, 5.7065, 21, 0, true, 0, true], - [3.5602, 190, 1.1422, 18, 4], - [3.5602, 211, 1.1422, 18, 4], - [3.5602, 232, 1.1422, 18, 4], - [0.1359, 252, 5.7065, 21, 0, true, 0, true], - [2.4181, 253, 2.2843, 18, 4], - [3.5602, 274, 1.1422, 18, 4], - [3.5602, 295, 1.1422, 18, 4], - [0.1359, 315, 5.7065, 21, 0, true, 0, true], - [2.4181, 316, 2.2843, 18, 4], - [0.1359, 336, 5.7065, 21, 0, true, 0, true], - [2.4181, 337, 2.2843, 18, 4], - [0.1359, 357, 5.7065, 21, 0, true, 0, true], - [2.4181, 358, 2.2843, 18, 4], - [0.1359, 378, 5.7065, 21, 0, true, 0, true], - [2.4181, 379, 2.2843, 18, 4], - [0.1359, 399, 5.7065, 21, 0, true, 0, true], - [2.4181, 400, 2.2843, 18, 4], - [8.1246, 85, 61.6381, 18, 4], - [8.1246, 106, 66.2024, 18, 4], - [8.1246, 127, 49.0808, 18, 4], - [8.1246, 169, 55.9358, 18, 4], - [8.1246, 190, 54.7958, 18, 4], - [8.1246, 232, 28.5369, 18, 4], - [8.1246, 253, 52.5093, 18, 4], - [8.1246, 274, 10.2751, 18, 4], - [8.1246, 316, 10.2751, 18, 4], - [8.1246, 337, 26.2568, 18, 4], - [8.1246, 358, 35.3855, 18, 4], - [8.1246, 379, 13.7016, 18, 4], - [8.1246, 400, 14.8416, 18, 4] - ] - }, - "1024": { - "name": "repo-difffilecard--skeleton-fixture", - "viewportWidth": 992, - "width": 992, - "height": 421, - "bones": [ - [0, 0, 100, 421, 0, true, 0, true], - [0, 32, 100, 42, "4px 4px 0px 0px", true, 15, false], - [1.1089, 46, 1.4113, 14, 0], - [3.3266, 41, 4.5174, 24, 4], - [8.6505, 45, 20.7898, 17, 4], - [0, 42, 100, 379, "0px 0px 4px 4px", true, 14, false], - [0.1008, 84, 4.2339, 21, 0, true, 0, true], - [2.6414, 85, 0.8474, 18, 4], - [0.1008, 105, 4.2339, 21, 0, true, 0, true], - [2.6414, 106, 0.8474, 18, 4], - [2.6414, 127, 0.8474, 18, 4], - [2.6414, 148, 0.8474, 18, 4], - [0.1008, 168, 4.2339, 21, 0, true, 0, true], - [2.6414, 169, 0.8474, 18, 4], - [0.1008, 189, 4.2339, 21, 0, true, 0, true], - [2.6414, 190, 0.8474, 18, 4], - [2.6414, 211, 0.8474, 18, 4], - [2.6414, 232, 0.8474, 18, 4], - [0.1008, 252, 4.2339, 21, 0, true, 0, true], - [1.794, 253, 1.6948, 18, 4], - [2.6414, 274, 0.8474, 18, 4], - [2.6414, 295, 0.8474, 18, 4], - [0.1008, 315, 4.2339, 21, 0, true, 0, true], - [1.794, 316, 1.6948, 18, 4], - [0.1008, 336, 4.2339, 21, 0, true, 0, true], - [1.794, 337, 1.6948, 18, 4], - [0.1008, 357, 4.2339, 21, 0, true, 0, true], - [1.794, 358, 1.6948, 18, 4], - [0.1008, 378, 4.2339, 21, 0, true, 0, true], - [1.794, 379, 1.6948, 18, 4], - [0.1008, 399, 4.2339, 21, 0, true, 0, true], - [1.794, 400, 1.6948, 18, 4], - [6.0279, 85, 45.7315, 18, 4], - [6.0279, 106, 49.1179, 18, 4], - [6.0279, 127, 36.4148, 18, 4], - [6.0279, 169, 41.5008, 18, 4], - [6.0279, 190, 40.6549, 18, 4], - [6.0279, 232, 21.1725, 18, 4], - [6.0279, 253, 38.9585, 18, 4], - [6.0279, 274, 7.6235, 18, 4], - [6.0279, 316, 7.6235, 18, 4], - [6.0279, 337, 19.4808, 18, 4], - [6.0279, 358, 26.2538, 18, 4], - [6.0279, 379, 10.1657, 18, 4], - [6.0279, 400, 11.0115, 18, 4] - ] - }, - "1440": { - "name": "repo-difffilecard--skeleton-fixture", - "viewportWidth": 1408, - "width": 1408, - "height": 421, - "bones": [ - [0, 0, 100, 421, 0, true, 0, true], - [0, 32, 100, 42, "4px 4px 0px 0px", true, 15, false], - [0.7813, 46, 0.9943, 14, 0], - [2.3438, 41, 3.1827, 24, 4], - [6.0946, 45, 14.6473, 17, 4], - [0, 42, 100, 379, "0px 0px 4px 4px", true, 14, false], - [0.071, 84, 2.983, 21, 0, true, 0, true], - [1.861, 85, 0.597, 18, 4], - [0.071, 105, 2.983, 21, 0, true, 0, true], - [1.861, 106, 0.597, 18, 4], - [1.861, 127, 0.597, 18, 4], - [1.861, 148, 0.597, 18, 4], - [0.071, 168, 2.983, 21, 0, true, 0, true], - [1.861, 169, 0.597, 18, 4], - [0.071, 189, 2.983, 21, 0, true, 0, true], - [1.861, 190, 0.597, 18, 4], - [1.861, 211, 0.597, 18, 4], - [1.861, 232, 0.597, 18, 4], - [0.071, 252, 2.983, 21, 0, true, 0, true], - [1.264, 253, 1.1941, 18, 4], - [1.861, 274, 0.597, 18, 4], - [1.861, 295, 0.597, 18, 4], - [0.071, 315, 2.983, 21, 0, true, 0, true], - [1.264, 316, 1.1941, 18, 4], - [0.071, 336, 2.983, 21, 0, true, 0, true], - [1.264, 337, 1.1941, 18, 4], - [0.071, 357, 2.983, 21, 0, true, 0, true], - [1.264, 358, 1.1941, 18, 4], - [0.071, 378, 2.983, 21, 0, true, 0, true], - [1.264, 379, 1.1941, 18, 4], - [0.071, 399, 2.983, 21, 0, true, 0, true], - [1.264, 400, 1.1941, 18, 4], - [4.2469, 85, 32.2199, 18, 4], - [4.2469, 106, 34.6058, 18, 4], - [4.2469, 127, 25.6559, 18, 4], - [4.2469, 169, 29.2392, 18, 4], - [4.2469, 190, 28.6432, 18, 4], - [4.2469, 232, 14.917, 18, 4], - [4.2469, 253, 27.4481, 18, 4], - [4.2469, 274, 5.3711, 18, 4], - [4.2469, 316, 5.3711, 18, 4], - [4.2469, 337, 13.7251, 18, 4], - [4.2469, 358, 18.497, 18, 4], - [4.2469, 379, 7.1622, 18, 4], - [4.2469, 400, 7.7581, 18, 4] - ] - } - }, - "_hash": "149c762d64c4aba69d5db3318f951ab8" -} diff --git a/web/src/lib/bones/repo-diffview--skeleton-fixture.bones.json b/web/src/lib/bones/repo-diffview--skeleton-fixture.bones.json deleted file mode 100644 index 73d72ed3c..000000000 --- a/web/src/lib/bones/repo-diffview--skeleton-fixture.bones.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "breakpoints": { - "375": { - "name": "repo-diffview--skeleton-fixture", - "viewportWidth": 343, - "width": 343, - "height": 308, - "bones": [ - [1.1662, 15, 4.9016, 18, 4], - [8.4001, 15, 4.9016, 18, 4], - [39.0671, 6, 12.2449, 36, 4], - [53.6443, 6, 12.2449, 36, 4], - [68.2216, 8, 9.9125, 32, 4], - [80.4665, 7, 19.5335, 34, 4, true, 15, true], - [80.4665, 8, 9.9125, 32, "4px 0px 0px 4px"], - [90.0875, 8, 9.9125, 32, "0px 4px 4px 0px"], - [0, 48, 100, 148, 0, true, 0, true], - [0, 48, 100, 42, "4px 4px 0px 0px", true, 15, false], - [3.207, 62, 4.0816, 14, 0], - [9.621, 57, 13.0649, 24, 4], - [25.0182, 61, 23.9568, 17, 4], - [0, 90, 100, 106, "0px 0px 4px 4px", true, 14, false], - [5.1886, 91, 2.4508, 18, 4], - [0.2915, 111, 9.7941, 21, 0, true, 0, true], - [5.1886, 112, 2.4508, 18, 4], - [0.2915, 132, 9.7941, 21, 0, true, 0, true], - [5.1886, 133, 2.4508, 18, 4], - [0.2915, 153, 9.7941, 21, 0, true, 0, true], - [5.1886, 154, 2.4508, 18, 4], - [5.1886, 175, 2.4508, 18, 4], - [14.9827, 91, 58.7874, 18, 4], - [14.9827, 112, 53.8903, 18, 4], - [14.9827, 133, 78.3801, 18, 4], - [14.9827, 154, 75.9293, 18, 4], - [14.9827, 175, 4.9016, 18, 4], - [0, 212, 100, 96, 0, true, 0, true], - [0, 212, 100, 39, "4px 4px 0px 0px", true, 15, false], - [3.207, 225, 4.0816, 14, 0], - [9.621, 223, 30.8765, 17, 4], - [0, 251, 100, 57, "0px 0px 4px 4px", true, 14, false], - [6.9698, 270, 86.0605, 17, 4] - ] - }, - "768": { - "name": "repo-diffview--skeleton-fixture", - "viewportWidth": 736, - "width": 736, - "height": 308, - "bones": [ - [0, 8, 4.6196, 32, 4], - [6.25, 15, 2.2843, 18, 4], - [9.6213, 15, 2.2843, 18, 4], - [13.536, 16, 11.8907, 15, 4], - [32.4261, 6, 21.4186, 36, 4], - [54.9316, 6, 19.3126, 36, 4], - [75.3312, 8, 14.4786, 32, 4], - [90.8967, 7, 9.1033, 34, 4, true, 15, true], - [90.8967, 8, 4.6196, 32, "4px 0px 0px 4px"], - [95.3804, 8, 4.6196, 32, "0px 4px 4px 0px"], - [0, 48, 26.5243, 260, 4, true, 15, true], - [5.2989, 73, 2.1739, 16, 0], - [8.5598, 72, 2.7768, 17, 4], - [6.3859, 103, 14.5678, 36, 0, true, 8, false], - [8.6957, 113, 2.1739, 16, 0], - [11.9565, 112, 7.7042, 17, 4], - [5.2989, 153, 2.1739, 16, 0], - [8.5598, 152, 5.7936, 17, 4], - [6.3859, 183, 14.5678, 36, 0, true, 8, false], - [8.6957, 193, 2.1739, 16, 0], - [11.9565, 192, 7.9102, 17, 4], - [28.6982, 48, 71.3018, 148, 0, true, 0, true], - [28.6982, 48, 71.3018, 42, "4px 4px 0px 0px", true, 15, false], - [30.1928, 62, 1.9022, 14, 0], - [33.1819, 57, 6.0887, 24, 4], - [40.3575, 61, 11.1647, 17, 4], - [28.6982, 90, 71.3018, 106, "0px 0px 4px 4px", true, 14, false], - [31.1163, 91, 1.1422, 18, 4], - [28.8341, 111, 4.5644, 21, 0, true, 0, true], - [31.1163, 112, 1.1422, 18, 4], - [28.8341, 132, 4.5644, 21, 0, true, 0, true], - [31.1163, 133, 1.1422, 18, 4], - [28.8341, 153, 4.5644, 21, 0, true, 0, true], - [31.1163, 154, 1.1422, 18, 4], - [31.1163, 175, 1.1422, 18, 4], - [35.6806, 91, 27.3968, 18, 4], - [35.6806, 112, 25.1146, 18, 4], - [35.6806, 133, 36.5277, 18, 4], - [35.6806, 154, 35.3855, 18, 4], - [35.6806, 175, 2.2843, 18, 4], - [28.6982, 212, 71.3018, 96, 0, true, 0, true], - [28.6982, 212, 71.3018, 39, "4px 4px 0px 0px", true, 15, false], - [30.1928, 225, 1.9022, 14, 0], - [33.1819, 223, 14.3894, 17, 4], - [28.6982, 251, 71.3018, 57, "0px 0px 4px 4px", true, 14, false], - [44.2956, 270, 40.107, 17, 4] - ] - }, - "1024": { - "name": "repo-diffview--skeleton-fixture", - "viewportWidth": 992, - "width": 992, - "height": 308, - "bones": [ - [0, 8, 3.4274, 32, 4], - [4.6371, 15, 1.6948, 18, 4], - [7.1384, 15, 1.6948, 18, 4], - [10.0428, 16, 8.8221, 15, 4], - [49.8645, 6, 15.8912, 36, 4], - [66.5622, 6, 14.3287, 36, 4], - [81.6973, 8, 10.7422, 32, 4], - [93.246, 7, 6.754, 34, 4, true, 15, true], - [93.246, 8, 3.4274, 32, "4px 0px 0px 4px"], - [96.5726, 8, 3.4274, 32, "0px 4px 4px 0px"], - [0, 48, 19.6793, 260, 4, true, 15, true], - [3.9315, 73, 1.6129, 16, 0], - [6.3508, 72, 2.0602, 17, 4], - [4.7379, 103, 10.8083, 36, 0, true, 8, false], - [6.4516, 113, 1.6129, 16, 0], - [8.871, 112, 5.716, 17, 4], - [3.9315, 153, 1.6129, 16, 0], - [6.3508, 152, 4.2985, 17, 4], - [4.7379, 183, 10.8083, 36, 0, true, 8, false], - [6.4516, 193, 1.6129, 16, 0], - [8.871, 192, 5.8688, 17, 4], - [21.2922, 48, 78.7078, 148, 0, true, 0, true], - [21.2922, 48, 78.7078, 42, "4px 4px 0px 0px", true, 15, false], - [22.4011, 62, 1.4113, 14, 0], - [24.6188, 57, 4.5174, 24, 4], - [29.9427, 61, 8.2835, 17, 4], - [21.2922, 90, 78.7078, 106, "0px 0px 4px 4px", true, 14, false], - [23.0863, 91, 0.8474, 18, 4], - [21.393, 111, 3.3865, 21, 0, true, 0, true], - [23.0863, 112, 0.8474, 18, 4], - [21.393, 132, 3.3865, 21, 0, true, 0, true], - [23.0863, 133, 0.8474, 18, 4], - [21.393, 153, 3.3865, 21, 0, true, 0, true], - [23.0863, 154, 0.8474, 18, 4], - [23.0863, 175, 0.8474, 18, 4], - [26.4727, 91, 20.3267, 18, 4], - [26.4727, 112, 18.6334, 18, 4], - [26.4727, 133, 27.1012, 18, 4], - [26.4727, 154, 26.2538, 18, 4], - [26.4727, 175, 1.6948, 18, 4], - [21.2922, 212, 78.7078, 96, 0, true, 0, true], - [21.2922, 212, 78.7078, 39, "4px 4px 0px 0px", true, 15, false], - [22.4011, 225, 1.4113, 14, 0], - [24.6188, 223, 10.676, 17, 4], - [21.2922, 251, 78.7078, 57, "0px 0px 4px 4px", true, 14, false], - [45.7677, 270, 29.7568, 17, 4] - ] - }, - "1440": { - "name": "repo-diffview--skeleton-fixture", - "viewportWidth": 1408, - "width": 1408, - "height": 308, - "bones": [ - [0, 8, 2.4148, 32, 4], - [3.267, 15, 1.1941, 18, 4], - [5.0293, 15, 1.1941, 18, 4], - [7.0756, 16, 6.2156, 15, 4], - [64.6773, 6, 11.1961, 36, 4], - [76.4415, 6, 10.0952, 36, 4], - [87.1049, 8, 7.5684, 32, 4], - [95.2415, 7, 4.7585, 34, 4, true, 15, true], - [95.2415, 8, 2.4148, 32, "4px 0px 0px 4px"], - [97.5852, 8, 2.4148, 32, "0px 4px 4px 0px"], - [0, 48, 13.865, 260, 4, true, 15, true], - [2.7699, 73, 1.1364, 16, 0], - [4.4744, 72, 1.4515, 17, 4], - [3.3381, 103, 7.615, 36, 0, true, 8, false], - [4.5455, 113, 1.1364, 16, 0], - [6.25, 112, 4.0272, 17, 4], - [2.7699, 153, 1.1364, 16, 0], - [4.4744, 152, 3.0285, 17, 4], - [3.3381, 183, 7.615, 36, 0, true, 8, false], - [4.5455, 193, 1.1364, 16, 0], - [6.25, 192, 4.1349, 17, 4], - [15.0013, 48, 84.9987, 148, 0, true, 0, true], - [15.0013, 48, 84.9987, 42, "4px 4px 0px 0px", true, 15, false], - [15.7826, 62, 0.9943, 14, 0], - [17.3451, 57, 3.1827, 24, 4], - [21.096, 61, 5.8361, 17, 4], - [15.0013, 90, 84.9987, 106, "0px 0px 4px 4px", true, 14, false], - [16.2653, 91, 0.597, 18, 4], - [15.0724, 111, 2.3859, 21, 0, true, 0, true], - [16.2653, 112, 0.597, 18, 4], - [15.0724, 132, 2.3859, 21, 0, true, 0, true], - [16.2653, 133, 0.597, 18, 4], - [15.0724, 153, 2.3859, 21, 0, true, 0, true], - [16.2653, 154, 0.597, 18, 4], - [16.2653, 175, 0.597, 18, 4], - [18.6512, 91, 14.3211, 18, 4], - [18.6512, 112, 13.1281, 18, 4], - [18.6512, 133, 19.094, 18, 4], - [18.6512, 154, 18.497, 18, 4], - [18.6512, 175, 1.1941, 18, 4], - [15.0013, 212, 84.9987, 96, 0, true, 0, true], - [15.0013, 212, 84.9987, 39, "4px 4px 0px 0px", true, 15, false], - [15.7826, 225, 0.9943, 14, 0], - [17.3451, 223, 7.5218, 17, 4], - [15.0013, 251, 84.9987, 57, "0px 0px 4px 4px", true, 14, false], - [47.0182, 270, 20.965, 17, 4] - ] - } - }, - "_hash": "21d83ecb9970d8828f06c7574a7b1a65" -} diff --git a/web/src/lib/bones/repo-pulls-pulldiff--skeleton-fixture.bones.json b/web/src/lib/bones/repo-pulls-pulldiff--skeleton-fixture.bones.json index 2e92e9d1a..7e9717d33 100644 --- a/web/src/lib/bones/repo-pulls-pulldiff--skeleton-fixture.bones.json +++ b/web/src/lib/bones/repo-pulls-pulldiff--skeleton-fixture.bones.json @@ -4,9 +4,9 @@ "name": "repo-pulls-pulldiff--skeleton-fixture", "viewportWidth": 343, "width": 343, - "height": 2173, + "height": 842, "bones": [ - [0, 0, 100, 2173, 4, true, 15, true], + [0, 0, 100, 842, 4, true, 15, true], [2.6239, 38, 12.2449, 36, 4], [6.7055, 99, 4.6647, 16, 0], [13.7026, 98, 5.9585, 17, 4], @@ -39,16 +39,16 @@ [16.3265, 495, 74.9271, 33, 0, true, 8, false], [21.2828, 504, 4.6647, 16, 0], [28.2799, 503, 24.9043, 17, 4], - [98.2507, 1, 0.2915, 2171, 4] + [98.2507, 1, 0.2915, 840, 4] ] }, "768": { "name": "repo-pulls-pulldiff--skeleton-fixture", "viewportWidth": 736, "width": 736, - "height": 2173, + "height": 842, "bones": [ - [0, 0, 100, 2173, 4, true, 15, true], + [0, 0, 100, 842, 4, true, 15, true], [1.2228, 38, 5.7065, 36, 4], [3.125, 99, 2.1739, 16, 0], [6.3859, 98, 2.7768, 17, 4], @@ -81,289 +81,113 @@ [7.6087, 495, 34.9185, 33, 0, true, 8, false], [9.9185, 504, 2.1739, 16, 0], [13.1793, 503, 11.6062, 17, 4], - [45.788, 1, 0.1359, 2171, 4], - [48.0978, 59, 3.4243, 18, 4], - [52.6091, 59, 3.4243, 18, 4], - [58.2074, 38, 1.2334, 17, 4], - [58.2074, 59, 7.8443, 17, 4], - [58.2074, 80, 3.7746, 17, 4], - [66.0517, 49, 14.7185, 38, 4], - [81.8572, 49, 18.1428, 38, 4, true, 15, true], - [81.8572, 50, 13.2324, 36, "4px 0px 0px 4px"], - [94.9537, 50, 5.0463, 36, "0px 4px 4px 0px"], - [47.0109, 124, 51.7663, 505, "0px 0px 4px 4px", true, 14, false], - [50.5711, 125, 1.1422, 18, 4], - [47.1467, 145, 5.7065, 21, 0, true, 0, true], - [50.5711, 146, 1.1422, 18, 4], - [47.1467, 166, 5.7065, 21, 0, true, 0, true], - [50.5711, 167, 1.1422, 18, 4], - [47.1467, 187, 5.7065, 21, 0, true, 0, true], - [50.5711, 188, 1.1422, 18, 4], - [50.5711, 209, 1.1422, 18, 4], - [47.1467, 229, 5.7065, 21, 0, true, 0, true], - [50.5711, 230, 1.1422, 18, 4], - [50.5711, 251, 1.1422, 18, 4], - [50.5711, 272, 1.1422, 18, 4], - [47.1467, 292, 5.7065, 21, 0, true, 0, true], - [50.5711, 293, 1.1422, 18, 4], - [47.1467, 313, 5.7065, 21, 0, true, 0, true], - [50.5711, 314, 1.1422, 18, 4], - [50.5711, 335, 1.1422, 18, 4], - [49.4289, 356, 2.2843, 18, 4], - [47.1467, 376, 5.7065, 21, 0, true, 0, true], - [50.5711, 377, 1.1422, 18, 4], - [47.1467, 397, 5.7065, 21, 0, true, 0, true], - [49.4289, 398, 2.2843, 18, 4], - [47.1467, 418, 5.7065, 21, 0, true, 0, true], - [49.4289, 419, 2.2843, 18, 4], - [47.1467, 439, 5.7065, 21, 0, true, 0, true], - [49.4289, 440, 2.2843, 18, 4], - [47.1467, 460, 5.7065, 21, 0, true, 0, true], - [49.4289, 461, 2.2843, 18, 4], - [47.1467, 481, 5.7065, 21, 0, true, 0, true], - [49.4289, 482, 2.2843, 18, 4], - [47.1467, 502, 5.7065, 21, 0, true, 0, true], - [49.4289, 503, 2.2843, 18, 4], - [47.1467, 523, 5.7065, 21, 0, true, 0, true], - [49.4289, 524, 2.2843, 18, 4], - [47.1467, 544, 5.7065, 21, 0, true, 0, true], - [49.4289, 545, 2.2843, 18, 4], - [47.1467, 565, 5.7065, 21, 0, true, 0, true], - [49.4289, 566, 2.2843, 18, 4], - [47.1467, 586, 5.7065, 21, 0, true, 0, true], - [49.4289, 587, 2.2843, 18, 4], - [47.1467, 607, 5.7065, 21, 0, true, 0, true], - [49.4289, 608, 2.2843, 18, 4], - [55.1354, 125, 20.5481, 18, 4], - [55.1354, 146, 44.8646, 18, 4], - [55.1354, 167, 23.9725, 18, 4], - [55.1354, 188, 33.1033, 18, 4], - [55.1354, 209, 44.8646, 18, 4], - [55.1354, 230, 34.2434, 18, 4], - [55.1354, 251, 30.819, 18, 4], - [55.1354, 293, 44.8646, 18, 4], - [55.1354, 314, 44.8646, 18, 4], - [55.1354, 335, 10.2751, 18, 4], - [55.1354, 377, 14.8416, 18, 4], - [57.4198, 398, 22.8282, 18, 4], - [55.1354, 419, 7.993, 18, 4], - [57.4198, 440, 21.6903, 18, 4], - [55.1354, 461, 5.7108, 18, 4], - [55.1354, 482, 10.2751, 18, 4], - [57.4198, 503, 23.9725, 18, 4], - [55.1354, 524, 44.5164, 18, 4], - [55.1354, 545, 13.7016, 18, 4], - [55.1354, 566, 36.5298, 18, 4], - [55.1354, 587, 43.3764, 18, 4], - [55.1354, 608, 11.4173, 18, 4], - [47.0109, 637, 51.7663, 44, "4px 4px 0px 0px", true, 15, false], - [48.5054, 652, 1.9022, 14, 0], - [51.4946, 651, 23.2995, 17, 4], - [76.4245, 650, 2.2843, 18, 4], - [79.7958, 650, 2.2843, 18, 4], - [47.0109, 681, 51.7663, 274, "0px 0px 4px 4px", true, 14, false], - [47.1467, 681, 5.7065, 21, 0, true, 0, true], - [50.5711, 682, 1.1422, 18, 4], - [47.1467, 702, 5.7065, 21, 0, true, 0, true], - [50.5711, 703, 1.1422, 18, 4], - [50.5711, 724, 1.1422, 18, 4], - [47.1467, 744, 5.7065, 21, 0, true, 0, true], - [50.5711, 745, 1.1422, 18, 4], - [47.1467, 765, 5.7065, 21, 0, true, 0, true], - [50.5711, 766, 1.1422, 18, 4], - [47.1467, 786, 5.7065, 21, 0, true, 0, true], - [50.5711, 787, 1.1422, 18, 4], - [47.1467, 807, 5.7065, 21, 0, true, 0, true], - [50.5711, 808, 1.1422, 18, 4], - [47.1467, 828, 5.7065, 21, 0, true, 0, true], - [50.5711, 829, 1.1422, 18, 4], - [47.1467, 849, 5.7065, 21, 0, true, 0, true], - [50.5711, 850, 1.1422, 18, 4], - [47.1467, 870, 5.7065, 21, 0, true, 0, true], - [50.5711, 871, 1.1422, 18, 4], - [47.1467, 891, 5.7065, 21, 0, true, 0, true], - [50.5711, 892, 1.1422, 18, 4], - [47.1467, 912, 5.7065, 21, 0, true, 0, true], - [49.4289, 913, 2.2843, 18, 4], - [49.4289, 934, 2.2843, 18, 4], - [55.1354, 682, 44.8646, 18, 4], - [52.8533, 702, 47.1467, 21, 0], - [55.1354, 724, 39.952, 18, 4], - [57.4198, 745, 42.2363, 18, 4], - [57.4198, 766, 18.2638, 18, 4], - [57.4198, 787, 27.3989, 18, 4], - [57.4198, 808, 5.7086, 18, 4], - [55.1354, 829, 42.2363, 18, 4], - [55.1354, 850, 22.8303, 18, 4], - [55.1354, 871, 12.5594, 18, 4], - [55.1354, 892, 15.9817, 18, 4], - [55.1354, 913, 3.4265, 18, 4], - [55.1354, 934, 2.2843, 18, 4], - [47.0109, 963, 51.7663, 44, "4px 4px 0px 0px", true, 15, false], - [48.5054, 978, 1.9022, 14, 0], - [51.4946, 977, 48.5054, 17, 4], - [47.0109, 1007, 51.7663, 673, "0px 0px 4px 4px", true, 14, false], - [50.5711, 1008, 1.1422, 18, 4], - [47.1467, 1028, 5.7065, 21, 0, true, 0, true], - [50.5711, 1029, 1.1422, 18, 4], - [47.1467, 1049, 5.7065, 21, 0, true, 0, true], - [50.5711, 1050, 1.1422, 18, 4], - [50.5711, 1071, 1.1422, 18, 4], - [47.1467, 1091, 5.7065, 21, 0, true, 0, true], - [50.5711, 1092, 1.1422, 18, 4], - [47.1467, 1112, 5.7065, 21, 0, true, 0, true], - [50.5711, 1113, 1.1422, 18, 4], - [47.1467, 1133, 5.7065, 21, 0, true, 0, true], - [50.5711, 1134, 1.1422, 18, 4], - [47.1467, 1154, 5.7065, 21, 0, true, 0, true], - [50.5711, 1155, 1.1422, 18, 4], - [47.1467, 1175, 5.7065, 21, 0, true, 0, true], - [50.5711, 1176, 1.1422, 18, 4], - [47.1467, 1196, 5.7065, 21, 0, true, 0, true], - [50.5711, 1197, 1.1422, 18, 4], - [47.1467, 1217, 5.7065, 21, 0, true, 0, true], - [49.4289, 1218, 2.2843, 18, 4], - [47.1467, 1238, 5.7065, 21, 0, true, 0, true], - [49.4289, 1239, 2.2843, 18, 4], - [47.1467, 1259, 5.7065, 21, 0, true, 0, true], - [49.4289, 1260, 2.2843, 18, 4], - [49.4289, 1281, 2.2843, 18, 4], - [49.4289, 1302, 2.2843, 18, 4], - [47.1467, 1322, 5.7065, 21, 0, true, 0, true], - [50.5711, 1323, 1.1422, 18, 4], - [47.1467, 1343, 5.7065, 21, 0, true, 0, true], - [50.5711, 1344, 1.1422, 18, 4], - [47.1467, 1364, 5.7065, 21, 0, true, 0, true], - [50.5711, 1365, 1.1422, 18, 4], - [47.1467, 1385, 5.7065, 21, 0, true, 0, true], - [50.5711, 1386, 1.1422, 18, 4], - [47.1467, 1406, 5.7065, 21, 0, true, 0, true], - [49.4289, 1407, 2.2843, 18, 4], - [47.1467, 1427, 5.7065, 21, 0, true, 0, true], - [49.4289, 1428, 2.2843, 18, 4], - [47.1467, 1448, 5.7065, 21, 0, true, 0, true], - [49.4289, 1449, 2.2843, 18, 4], - [47.1467, 1469, 5.7065, 21, 0, true, 0, true], - [49.4289, 1470, 2.2843, 18, 4], - [47.1467, 1490, 5.7065, 21, 0, true, 0, true], - [49.4289, 1491, 2.2843, 18, 4], - [47.1467, 1511, 5.7065, 21, 0, true, 0, true], - [49.4289, 1512, 2.2843, 18, 4], - [47.1467, 1532, 5.7065, 21, 0, true, 0, true], - [49.4289, 1533, 2.2843, 18, 4], - [47.1467, 1553, 5.7065, 21, 0, true, 0, true], - [49.4289, 1554, 2.2843, 18, 4], - [47.1467, 1574, 5.7065, 21, 0, true, 0, true], - [49.4289, 1575, 2.2843, 18, 4], - [47.1467, 1595, 5.7065, 21, 0, true, 0, true], - [49.4289, 1596, 2.2843, 18, 4], - [47.1467, 1616, 5.7065, 21, 0, true, 0, true], - [49.4289, 1617, 2.2843, 18, 4], - [47.1467, 1637, 5.7065, 21, 0, true, 0, true], - [49.4289, 1638, 2.2843, 18, 4], - [47.1467, 1658, 5.7065, 21, 0, true, 0, true], - [49.4289, 1659, 2.2843, 18, 4], - [55.1354, 1008, 20.5481, 18, 4], - [55.1354, 1029, 44.8646, 18, 4], - [55.1354, 1050, 44.8646, 18, 4], - [55.1354, 1071, 44.8646, 18, 4], - [55.1354, 1092, 44.8646, 18, 4], - [52.8533, 1112, 47.1467, 21, 0], - [55.1354, 1134, 21.6903, 18, 4], - [55.1354, 1155, 44.8646, 18, 4], - [55.1354, 1176, 25.1146, 18, 4], - [55.1354, 1197, 21.6903, 18, 4], - [55.1354, 1218, 3.4265, 18, 4], - [52.8533, 1238, 47.1467, 21, 0], - [55.1354, 1260, 44.8646, 18, 4], - [55.1354, 1281, 10.2751, 18, 4], - [55.1354, 1323, 44.8646, 18, 4], - [57.4198, 1344, 29.679, 18, 4], - [55.1354, 1365, 44.8646, 18, 4], - [57.4198, 1386, 7.993, 18, 4], - [55.1354, 1407, 6.8508, 18, 4], - [55.1354, 1428, 14.8416, 18, 4], - [57.4198, 1449, 37.6656, 18, 4], - [55.1354, 1470, 7.993, 18, 4], - [57.4198, 1491, 42.5802, 18, 4], - [55.1354, 1512, 17.1259, 18, 4], - [55.1354, 1533, 44.8646, 18, 4], - [55.1354, 1554, 10.2773, 18, 4], - [55.1354, 1575, 43.3785, 18, 4], - [55.1354, 1596, 44.8646, 18, 4], - [55.1354, 1617, 12.5594, 18, 4], - [55.1354, 1638, 9.1351, 18, 4], - [55.1354, 1659, 5.7108, 18, 4], - [47.0109, 1688, 51.7663, 44, "4px 4px 0px 0px", true, 15, false], - [48.5054, 1703, 1.9022, 14, 0], - [51.4946, 1702, 21.4207, 17, 4], - [74.5457, 1701, 2.2843, 18, 4], - [77.9169, 1701, 2.2843, 18, 4], - [47.0109, 1732, 51.7663, 127, "0px 0px 4px 4px", true, 14, false], - [49.4289, 1733, 1.1422, 18, 4], - [49.4289, 1754, 1.1422, 18, 4], - [47.1467, 1774, 4.5644, 21, 0, true, 0, true], - [49.4289, 1775, 1.1422, 18, 4], - [47.1467, 1795, 4.5644, 21, 0, true, 0, true], - [49.4289, 1796, 1.1422, 18, 4], - [47.1467, 1816, 4.5644, 21, 0, true, 0, true], - [49.4289, 1817, 1.1422, 18, 4], - [49.4289, 1838, 1.1422, 18, 4], - [53.9933, 1733, 14.8395, 18, 4], - [53.9933, 1754, 26.2568, 18, 4], - [56.2776, 1775, 30.8233, 18, 4], - [56.2776, 1796, 33.1033, 18, 4], - [53.9933, 1817, 35.3855, 18, 4], - [53.9933, 1838, 2.2843, 18, 4], - [47.0109, 1867, 51.7663, 44, "4px 4px 0px 0px", true, 15, false], - [48.5054, 1882, 1.9022, 14, 0], - [51.4946, 1881, 14.3066, 17, 4], - [67.4316, 1880, 3.4243, 18, 4], - [71.9429, 1880, 2.2843, 18, 4], - [47.0109, 1911, 51.7663, 253, "0px 0px 4px 4px", true, 14, false], - [47.1467, 1911, 5.7065, 21, 0, true, 0, true], - [50.5711, 1912, 1.1422, 18, 4], - [47.1467, 1932, 5.7065, 21, 0, true, 0, true], - [50.5711, 1933, 1.1422, 18, 4], - [50.5711, 1954, 1.1422, 18, 4], - [47.1467, 1974, 5.7065, 21, 0, true, 0, true], - [50.5711, 1975, 1.1422, 18, 4], - [47.1467, 1995, 5.7065, 21, 0, true, 0, true], - [50.5711, 1996, 1.1422, 18, 4], - [47.1467, 2016, 5.7065, 21, 0, true, 0, true], - [50.5711, 2017, 1.1422, 18, 4], - [47.1467, 2037, 5.7065, 21, 0, true, 0, true], - [50.5711, 2038, 1.1422, 18, 4], - [50.5711, 2059, 1.1422, 18, 4], - [47.1467, 2079, 5.7065, 21, 0, true, 0, true], - [50.5711, 2080, 1.1422, 18, 4], - [47.1467, 2100, 5.7065, 21, 0, true, 0, true], - [50.5711, 2101, 1.1422, 18, 4], - [47.1467, 2121, 5.7065, 21, 0, true, 0, true], - [50.5711, 2122, 1.1422, 18, 4], - [47.1467, 2142, 5.7065, 21, 0, true, 0, true], - [49.4289, 2143, 2.2843, 18, 4], - [55.1354, 1912, 44.8646, 18, 4], - [52.8533, 1932, 47.1467, 21, 0], - [55.1354, 1954, 44.8646, 18, 4], - [55.1354, 1975, 44.8646, 18, 4], - [55.1354, 1996, 21.6903, 18, 4], - [55.1354, 2017, 37.6741, 18, 4], - [55.1354, 2038, 33.1055, 18, 4], - [55.1354, 2059, 2.2843, 18, 4], - [52.8533, 2079, 47.1467, 21, 0], - [55.1354, 2101, 44.8646, 18, 4], - [55.1354, 2122, 44.8646, 18, 4], - [55.1354, 2143, 2.2843, 18, 4] + [45.788, 1, 0.1359, 840, 4], + [48.0978, 32, 3.4243, 18, 4], + [52.6091, 32, 3.4243, 18, 4], + [58.2074, 11, 1.2334, 17, 4], + [58.2074, 32, 7.8443, 17, 4], + [58.2074, 53, 3.7746, 17, 4], + [67.1387, 22, 14.7185, 38, 4], + [82.9441, 22, 17.0559, 38, 4, true, 15, true], + [82.9441, 23, 13.2324, 36, "4px 0px 0px 4px"], + [47.0109, 80, 51.7663, 547, 4, true, 15, false], + [50.5711, 123, 1.1422, 18, 4], + [47.1467, 143, 5.7065, 21, 0, true, 0, true], + [50.5711, 144, 1.1422, 18, 4], + [47.1467, 164, 5.7065, 21, 0, true, 0, true], + [50.5711, 165, 1.1422, 18, 4], + [47.1467, 185, 5.7065, 21, 0, true, 0, true], + [50.5711, 186, 1.1422, 18, 4], + [50.5711, 207, 1.1422, 18, 4], + [47.1467, 227, 5.7065, 21, 0, true, 0, true], + [50.5711, 228, 1.1422, 18, 4], + [50.5711, 249, 1.1422, 18, 4], + [50.5711, 270, 1.1422, 18, 4], + [47.1467, 290, 5.7065, 21, 0, true, 0, true], + [50.5711, 291, 1.1422, 18, 4], + [47.1467, 311, 5.7065, 21, 0, true, 0, true], + [50.5711, 312, 1.1422, 18, 4], + [50.5711, 333, 1.1422, 18, 4], + [49.4289, 354, 2.2843, 18, 4], + [47.1467, 374, 5.7065, 21, 0, true, 0, true], + [50.5711, 375, 1.1422, 18, 4], + [47.1467, 395, 5.7065, 21, 0, true, 0, true], + [49.4289, 396, 2.2843, 18, 4], + [47.1467, 416, 5.7065, 21, 0, true, 0, true], + [49.4289, 417, 2.2843, 18, 4], + [47.1467, 437, 5.7065, 21, 0, true, 0, true], + [49.4289, 438, 2.2843, 18, 4], + [47.1467, 458, 5.7065, 21, 0, true, 0, true], + [49.4289, 459, 2.2843, 18, 4], + [47.1467, 479, 5.7065, 21, 0, true, 0, true], + [49.4289, 480, 2.2843, 18, 4], + [47.1467, 500, 5.7065, 21, 0, true, 0, true], + [49.4289, 501, 2.2843, 18, 4], + [47.1467, 521, 5.7065, 21, 0, true, 0, true], + [49.4289, 522, 2.2843, 18, 4], + [47.1467, 542, 5.7065, 21, 0, true, 0, true], + [49.4289, 543, 2.2843, 18, 4], + [47.1467, 563, 5.7065, 21, 0, true, 0, true], + [49.4289, 564, 2.2843, 18, 4], + [47.1467, 584, 5.7065, 21, 0, true, 0, true], + [49.4289, 585, 2.2843, 18, 4], + [47.1467, 605, 5.7065, 21, 0, true, 0, true], + [49.4289, 606, 2.2843, 18, 4], + [55.1354, 123, 21.6861, 18, 4], + [55.1354, 144, 44.8646, 18, 4], + [55.1354, 165, 44.8646, 18, 4], + [55.1354, 186, 2.2843, 18, 4], + [55.1354, 207, 44.8646, 18, 4], + [55.1354, 228, 44.8646, 18, 4], + [55.1354, 249, 44.8646, 18, 4], + [55.1354, 291, 44.8646, 18, 4], + [55.1354, 312, 44.8646, 18, 4], + [55.1354, 333, 14.8374, 18, 4], + [55.1354, 375, 14.8416, 18, 4], + [57.4198, 396, 22.8282, 18, 4], + [55.1354, 417, 7.993, 18, 4], + [57.4198, 438, 21.6903, 18, 4], + [55.1354, 459, 5.7108, 18, 4], + [55.1354, 480, 10.2751, 18, 4], + [57.4198, 501, 23.9725, 18, 4], + [55.1354, 522, 44.5164, 18, 4], + [55.1354, 543, 13.7016, 18, 4], + [55.1354, 564, 36.5298, 18, 4], + [55.1354, 585, 43.3764, 18, 4], + [55.1354, 606, 11.4173, 18, 4], + [47.0109, 635, 51.7663, 207, 4, true, 15, false], + [47.1467, 677, 5.7065, 21, 0, true, 0, true], + [50.5711, 678, 1.1422, 18, 4], + [47.1467, 698, 5.7065, 21, 0, true, 0, true], + [50.5711, 699, 1.1422, 18, 4], + [50.5711, 720, 1.1422, 18, 4], + [47.1467, 740, 5.7065, 21, 0, true, 0, true], + [50.5711, 741, 1.1422, 18, 4], + [47.1467, 761, 5.7065, 21, 0, true, 0, true], + [50.5711, 762, 1.1422, 18, 4], + [47.1467, 782, 5.7065, 21, 0, true, 0, true], + [50.5711, 783, 1.1422, 18, 4], + [47.1467, 803, 5.7065, 21, 0, true, 0, true], + [50.5711, 804, 1.1422, 18, 4], + [47.1467, 824, 5.7065, 18, 0, true, 0, true], + [50.5711, 825, 1.1422, 17, 4], + [55.1354, 678, 44.8646, 18, 4], + [52.8533, 698, 47.1467, 21, 0], + [55.1354, 720, 39.952, 18, 4], + [57.4198, 741, 42.2363, 18, 4], + [57.4198, 762, 18.2638, 18, 4], + [57.4198, 783, 27.3989, 18, 4], + [57.4198, 804, 5.7086, 18, 4], + [55.1354, 825, 42.2363, 17, 4] ] }, "1024": { "name": "repo-pulls-pulldiff--skeleton-fixture", "viewportWidth": 992, "width": 992, - "height": 2150, + "height": 842, "bones": [ - [0, 0, 100, 2150, 4, true, 15, true], + [0, 0, 100, 842, 4, true, 15, true], [0.9073, 38, 4.2339, 36, 4], [2.3185, 99, 1.6129, 16, 0], [4.7379, 98, 2.0602, 17, 4], @@ -396,285 +220,249 @@ [5.6452, 495, 25.9073, 33, 0, true, 8, false], [7.3589, 504, 1.6129, 16, 0], [9.7782, 503, 8.6111, 17, 4], - [33.9718, 1, 0.1008, 2148, 4], - [35.6855, 47, 2.5406, 18, 4], - [39.0326, 47, 2.5406, 18, 4], - [43.1861, 48, 10.2933, 17, 4], - [69.6462, 37, 10.9202, 38, 4], - [81.3729, 37, 17.7199, 38, 4, true, 15, true], - [81.3729, 38, 9.8176, 36, "4px 0px 0px 4px"], - [91.0897, 38, 8.0031, 36, "0px 4px 4px 0px"], - [34.879, 82, 64.2137, 44, "4px 4px 0px 0px", true, 15, false], - [35.9879, 97, 1.4113, 14, 0], - [38.2056, 96, 32.4534, 17, 4], - [71.8687, 95, 2.5406, 18, 4], - [75.2158, 95, 1.6948, 18, 4], - [34.879, 101, 64.2137, 505, "0px 0px 4px 4px", true, 14, false], - [34.9798, 122, 4.2339, 21, 0, true, 0, true], - [37.5205, 123, 0.8474, 18, 4], - [34.9798, 143, 4.2339, 21, 0, true, 0, true], - [37.5205, 144, 0.8474, 18, 4], - [34.9798, 164, 4.2339, 21, 0, true, 0, true], - [37.5205, 165, 0.8474, 18, 4], - [37.5205, 186, 0.8474, 18, 4], - [34.9798, 206, 4.2339, 21, 0, true, 0, true], - [37.5205, 207, 0.8474, 18, 4], - [37.5205, 228, 0.8474, 18, 4], - [37.5205, 249, 0.8474, 18, 4], - [34.9798, 269, 4.2339, 21, 0, true, 0, true], - [37.5205, 270, 0.8474, 18, 4], - [34.9798, 290, 4.2339, 21, 0, true, 0, true], - [37.5205, 291, 0.8474, 18, 4], - [37.5205, 312, 0.8474, 18, 4], - [36.6731, 333, 1.6948, 18, 4], - [34.9798, 353, 4.2339, 21, 0, true, 0, true], - [37.5205, 354, 0.8474, 18, 4], - [34.9798, 374, 4.2339, 21, 0, true, 0, true], - [36.6731, 375, 1.6948, 18, 4], - [34.9798, 395, 4.2339, 21, 0, true, 0, true], - [36.6731, 396, 1.6948, 18, 4], - [34.9798, 416, 4.2339, 21, 0, true, 0, true], - [36.6731, 417, 1.6948, 18, 4], - [34.9798, 437, 4.2339, 21, 0, true, 0, true], - [36.6731, 438, 1.6948, 18, 4], - [34.9798, 458, 4.2339, 21, 0, true, 0, true], - [36.6731, 459, 1.6948, 18, 4], - [34.9798, 479, 4.2339, 21, 0, true, 0, true], - [36.6731, 480, 1.6948, 18, 4], - [34.9798, 500, 4.2339, 21, 0, true, 0, true], - [36.6731, 501, 1.6948, 18, 4], - [34.9798, 521, 4.2339, 21, 0, true, 0, true], - [36.6731, 522, 1.6948, 18, 4], - [34.9798, 542, 4.2339, 21, 0, true, 0, true], - [36.6731, 543, 1.6948, 18, 4], - [34.9798, 563, 4.2339, 21, 0, true, 0, true], - [36.6731, 564, 1.6948, 18, 4], - [34.9798, 584, 4.2339, 21, 0, true, 0, true], - [36.6731, 585, 1.6948, 18, 4], - [40.9069, 123, 45.7315, 18, 4], - [40.9069, 144, 49.1179, 18, 4], - [40.9069, 165, 59.0931, 18, 4], - [40.9069, 186, 36.4148, 18, 4], - [40.9069, 207, 49.9638, 18, 4], - [40.9069, 228, 44.8825, 18, 4], - [40.9069, 270, 53.3581, 18, 4], - [40.9069, 291, 54.2039, 18, 4], - [40.9069, 312, 7.6235, 18, 4], - [40.9069, 354, 11.0115, 18, 4], - [42.6018, 375, 16.9371, 18, 4], - [40.9069, 396, 5.9303, 18, 4], - [42.6018, 417, 16.0928, 18, 4], - [40.9069, 438, 4.237, 18, 4], - [40.9069, 459, 7.6235, 18, 4], - [42.6018, 480, 17.786, 18, 4], - [40.9069, 501, 33.0283, 18, 4], - [40.9069, 522, 10.1657, 18, 4], - [40.9069, 543, 27.1028, 18, 4], - [40.9069, 564, 34.7231, 18, 4], - [40.9069, 585, 8.4709, 18, 4], - [34.879, 614, 64.2137, 44, "4px 4px 0px 0px", true, 15, false], - [35.9879, 629, 1.4113, 14, 0], - [38.2056, 628, 17.2867, 17, 4], - [56.7021, 627, 1.6948, 18, 4], - [59.2033, 627, 1.6948, 18, 4], - [34.879, 658, 64.2137, 274, "0px 0px 4px 4px", true, 14, false], - [34.9798, 658, 4.2339, 21, 0, true, 0, true], - [37.5205, 659, 0.8474, 18, 4], - [34.9798, 679, 4.2339, 21, 0, true, 0, true], - [37.5205, 680, 0.8474, 18, 4], - [37.5205, 701, 0.8474, 18, 4], - [34.9798, 721, 4.2339, 21, 0, true, 0, true], - [37.5205, 722, 0.8474, 18, 4], - [34.9798, 742, 4.2339, 21, 0, true, 0, true], - [37.5205, 743, 0.8474, 18, 4], - [34.9798, 763, 4.2339, 21, 0, true, 0, true], - [37.5205, 764, 0.8474, 18, 4], - [34.9798, 784, 4.2339, 21, 0, true, 0, true], - [37.5205, 785, 0.8474, 18, 4], - [34.9798, 805, 4.2339, 21, 0, true, 0, true], - [37.5205, 806, 0.8474, 18, 4], - [34.9798, 826, 4.2339, 21, 0, true, 0, true], - [37.5205, 827, 0.8474, 18, 4], - [34.9798, 847, 4.2339, 21, 0, true, 0, true], - [37.5205, 848, 0.8474, 18, 4], - [34.9798, 868, 4.2339, 21, 0, true, 0, true], - [37.5205, 869, 0.8474, 18, 4], - [34.9798, 889, 4.2339, 21, 0, true, 0, true], - [36.6731, 890, 1.6948, 18, 4], - [36.6731, 911, 1.6948, 18, 4], - [40.9069, 659, 36.4148, 18, 4], - [39.2137, 679, 59.7782, 21, 0], - [40.9069, 701, 40.6549, 18, 4], - [42.6018, 722, 32.184, 18, 4], - [42.6018, 743, 13.5506, 18, 4], - [42.6018, 764, 20.3283, 18, 4], - [42.6018, 785, 4.2354, 18, 4], - [40.9069, 806, 35.5705, 18, 4], - [40.9069, 827, 16.9386, 18, 4], - [40.9069, 848, 9.3183, 18, 4], - [40.9069, 869, 11.8574, 18, 4], - [40.9069, 890, 2.5422, 18, 4], - [40.9069, 911, 1.6948, 18, 4], - [34.879, 940, 64.2137, 44, "4px 4px 0px 0px", true, 15, false], - [35.9879, 955, 1.4113, 14, 0], - [38.2056, 954, 36.5171, 17, 4], - [75.9325, 953, 2.5406, 18, 4], - [79.2795, 953, 1.6948, 18, 4], - [34.879, 984, 64.2137, 673, "0px 0px 4px 4px", true, 14, false], - [37.5205, 985, 0.8474, 18, 4], - [34.9798, 1005, 4.2339, 21, 0, true, 0, true], - [37.5205, 1006, 0.8474, 18, 4], - [34.9798, 1026, 4.2339, 21, 0, true, 0, true], - [37.5205, 1027, 0.8474, 18, 4], - [37.5205, 1048, 0.8474, 18, 4], - [34.9798, 1068, 4.2339, 21, 0, true, 0, true], - [37.5205, 1069, 0.8474, 18, 4], - [34.9798, 1089, 4.2339, 21, 0, true, 0, true], - [37.5205, 1090, 0.8474, 18, 4], - [34.9798, 1110, 4.2339, 21, 0, true, 0, true], - [37.5205, 1111, 0.8474, 18, 4], - [34.9798, 1131, 4.2339, 21, 0, true, 0, true], - [37.5205, 1132, 0.8474, 18, 4], - [34.9798, 1152, 4.2339, 21, 0, true, 0, true], - [37.5205, 1153, 0.8474, 18, 4], - [34.9798, 1173, 4.2339, 21, 0, true, 0, true], - [37.5205, 1174, 0.8474, 18, 4], - [34.9798, 1194, 4.2339, 21, 0, true, 0, true], - [36.6731, 1195, 1.6948, 18, 4], - [34.9798, 1215, 4.2339, 21, 0, true, 0, true], - [36.6731, 1216, 1.6948, 18, 4], - [34.9798, 1236, 4.2339, 21, 0, true, 0, true], - [36.6731, 1237, 1.6948, 18, 4], - [36.6731, 1258, 1.6948, 18, 4], - [36.6731, 1279, 1.6948, 18, 4], - [34.9798, 1299, 4.2339, 21, 0, true, 0, true], - [37.5205, 1300, 0.8474, 18, 4], - [34.9798, 1320, 4.2339, 21, 0, true, 0, true], - [37.5205, 1321, 0.8474, 18, 4], - [34.9798, 1341, 4.2339, 21, 0, true, 0, true], - [37.5205, 1342, 0.8474, 18, 4], - [34.9798, 1362, 4.2339, 21, 0, true, 0, true], - [37.5205, 1363, 0.8474, 18, 4], - [34.9798, 1383, 4.2339, 21, 0, true, 0, true], - [36.6731, 1384, 1.6948, 18, 4], - [34.9798, 1404, 4.2339, 21, 0, true, 0, true], - [36.6731, 1405, 1.6948, 18, 4], - [34.9798, 1425, 4.2339, 21, 0, true, 0, true], - [36.6731, 1426, 1.6948, 18, 4], - [34.9798, 1446, 4.2339, 21, 0, true, 0, true], - [36.6731, 1447, 1.6948, 18, 4], - [34.9798, 1467, 4.2339, 21, 0, true, 0, true], - [36.6731, 1468, 1.6948, 18, 4], - [34.9798, 1488, 4.2339, 21, 0, true, 0, true], - [36.6731, 1489, 1.6948, 18, 4], - [34.9798, 1509, 4.2339, 21, 0, true, 0, true], - [36.6731, 1510, 1.6948, 18, 4], - [34.9798, 1530, 4.2339, 21, 0, true, 0, true], - [36.6731, 1531, 1.6948, 18, 4], - [34.9798, 1551, 4.2339, 21, 0, true, 0, true], - [36.6731, 1552, 1.6948, 18, 4], - [34.9798, 1572, 4.2339, 21, 0, true, 0, true], - [36.6731, 1573, 1.6948, 18, 4], - [34.9798, 1593, 4.2339, 21, 0, true, 0, true], - [36.6731, 1594, 1.6948, 18, 4], - [34.9798, 1614, 4.2339, 21, 0, true, 0, true], - [36.6731, 1615, 1.6948, 18, 4], - [34.9798, 1635, 4.2339, 21, 0, true, 0, true], - [36.6731, 1636, 1.6948, 18, 4], - [40.9069, 985, 15.2454, 18, 4], - [40.9069, 1006, 45.7315, 18, 4], - [40.9069, 1027, 59.0931, 18, 4], - [40.9069, 1048, 49.9638, 18, 4], - [40.9069, 1069, 33.8773, 18, 4], - [39.2137, 1089, 60.7863, 21, 0], - [40.9069, 1111, 16.0928, 18, 4], - [40.9069, 1132, 35.5674, 18, 4], - [40.9069, 1153, 18.6334, 18, 4], - [40.9069, 1174, 16.0928, 18, 4], - [40.9069, 1195, 2.5422, 18, 4], - [39.2137, 1215, 60.7863, 21, 0], - [40.9069, 1237, 59.0931, 18, 4], - [40.9069, 1258, 7.6235, 18, 4], - [40.9069, 1300, 52.5028, 18, 4], - [42.6018, 1321, 22.0199, 18, 4], - [40.9069, 1342, 34.7215, 18, 4], - [42.6018, 1363, 5.9303, 18, 4], - [40.9069, 1384, 5.0829, 18, 4], - [40.9069, 1405, 11.0115, 18, 4], - [42.6018, 1426, 27.9454, 18, 4], - [40.9069, 1447, 5.9303, 18, 4], - [42.6018, 1468, 52.5028, 18, 4], - [40.9069, 1489, 12.7063, 18, 4], - [40.9069, 1510, 49.1179, 18, 4], - [40.9069, 1531, 7.6251, 18, 4], - [40.9069, 1552, 32.184, 18, 4], - [40.9069, 1573, 36.4148, 18, 4], - [40.9069, 1594, 9.3183, 18, 4], - [40.9069, 1615, 6.7777, 18, 4], - [40.9069, 1636, 4.237, 18, 4], - [34.879, 1665, 64.2137, 44, "4px 4px 0px 0px", true, 15, false], - [35.9879, 1680, 1.4113, 14, 0], - [38.2056, 1679, 15.8928, 17, 4], - [55.3081, 1678, 1.6948, 18, 4], - [57.8093, 1678, 1.6948, 18, 4], - [34.879, 1709, 64.2137, 127, "0px 0px 4px 4px", true, 14, false], - [36.6731, 1710, 0.8474, 18, 4], - [36.6731, 1731, 0.8474, 18, 4], - [34.9798, 1751, 3.3865, 21, 0, true, 0, true], - [36.6731, 1752, 0.8474, 18, 4], - [34.9798, 1772, 3.3865, 21, 0, true, 0, true], - [36.6731, 1773, 0.8474, 18, 4], - [34.9798, 1793, 3.3865, 21, 0, true, 0, true], - [36.6731, 1794, 0.8474, 18, 4], - [36.6731, 1815, 0.8474, 18, 4], - [40.0595, 1710, 11.01, 18, 4], - [40.0595, 1731, 19.4808, 18, 4], - [41.7543, 1752, 22.8689, 18, 4], - [41.7543, 1773, 24.5605, 18, 4], - [40.0595, 1794, 26.2538, 18, 4], - [40.0595, 1815, 1.6948, 18, 4], - [34.879, 1844, 64.2137, 44, "4px 4px 0px 0px", true, 15, false], - [35.9879, 1859, 1.4113, 14, 0], - [38.2056, 1858, 10.6146, 17, 4], - [50.0299, 1857, 2.5406, 18, 4], - [53.377, 1857, 1.6948, 18, 4], - [34.879, 1888, 64.2137, 253, "0px 0px 4px 4px", true, 14, false], - [34.9798, 1888, 4.2339, 21, 0, true, 0, true], - [37.5205, 1889, 0.8474, 18, 4], - [34.9798, 1909, 4.2339, 21, 0, true, 0, true], - [37.5205, 1910, 0.8474, 18, 4], - [37.5205, 1931, 0.8474, 18, 4], - [34.9798, 1951, 4.2339, 21, 0, true, 0, true], - [37.5205, 1952, 0.8474, 18, 4], - [34.9798, 1972, 4.2339, 21, 0, true, 0, true], - [37.5205, 1973, 0.8474, 18, 4], - [34.9798, 1993, 4.2339, 21, 0, true, 0, true], - [37.5205, 1994, 0.8474, 18, 4], - [34.9798, 2014, 4.2339, 21, 0, true, 0, true], - [37.5205, 2015, 0.8474, 18, 4], - [37.5205, 2036, 0.8474, 18, 4], - [34.9798, 2056, 4.2339, 21, 0, true, 0, true], - [37.5205, 2057, 0.8474, 18, 4], - [34.9798, 2077, 4.2339, 21, 0, true, 0, true], - [37.5205, 2078, 0.8474, 18, 4], - [34.9798, 2098, 4.2339, 21, 0, true, 0, true], - [37.5205, 2099, 0.8474, 18, 4], - [34.9798, 2119, 4.2339, 21, 0, true, 0, true], - [36.6731, 2120, 1.6948, 18, 4], - [40.9069, 1889, 59.0931, 18, 4], - [39.2137, 1909, 60.7863, 21, 0], - [40.9069, 1931, 43.1956, 18, 4], - [40.9069, 1952, 44.0461, 18, 4], - [40.9069, 1973, 16.0928, 18, 4], - [40.9069, 1994, 27.9517, 18, 4], - [40.9069, 2015, 24.5621, 18, 4], - [40.9069, 2036, 1.6948, 18, 4], - [39.2137, 2056, 60.7863, 21, 0], - [40.9069, 2078, 48.2768, 18, 4], - [40.9069, 2099, 33.8804, 18, 4], - [40.9069, 2120, 1.6948, 18, 4] + [33.9718, 1, 0.1008, 840, 4], + [35.6855, 20, 2.5406, 18, 4], + [39.0326, 20, 2.5406, 18, 4], + [43.1861, 21, 10.2933, 17, 4], + [69.6462, 10, 10.9202, 38, 4], + [81.3729, 10, 17.7199, 38, 4, true, 15, true], + [81.3729, 11, 9.8176, 36, "4px 0px 0px 4px"], + [91.0897, 11, 8.0031, 36, "0px 4px 4px 0px"], + [34.879, 57, 64.2137, 547, 4, true, 15, false], + [37.5205, 100, 0.8474, 18, 4], + [34.9798, 120, 4.2339, 21, 0, true, 0, true], + [37.5205, 121, 0.8474, 18, 4], + [34.9798, 141, 4.2339, 21, 0, true, 0, true], + [37.5205, 142, 0.8474, 18, 4], + [34.9798, 162, 4.2339, 21, 0, true, 0, true], + [37.5205, 163, 0.8474, 18, 4], + [37.5205, 184, 0.8474, 18, 4], + [34.9798, 204, 4.2339, 21, 0, true, 0, true], + [37.5205, 205, 0.8474, 18, 4], + [37.5205, 226, 0.8474, 18, 4], + [37.5205, 247, 0.8474, 18, 4], + [34.9798, 267, 4.2339, 21, 0, true, 0, true], + [37.5205, 268, 0.8474, 18, 4], + [34.9798, 288, 4.2339, 21, 0, true, 0, true], + [37.5205, 289, 0.8474, 18, 4], + [37.5205, 310, 0.8474, 18, 4], + [36.6731, 331, 1.6948, 18, 4], + [34.9798, 351, 4.2339, 21, 0, true, 0, true], + [37.5205, 352, 0.8474, 18, 4], + [34.9798, 372, 4.2339, 21, 0, true, 0, true], + [36.6731, 373, 1.6948, 18, 4], + [34.9798, 393, 4.2339, 21, 0, true, 0, true], + [36.6731, 394, 1.6948, 18, 4], + [34.9798, 414, 4.2339, 21, 0, true, 0, true], + [36.6731, 415, 1.6948, 18, 4], + [34.9798, 435, 4.2339, 21, 0, true, 0, true], + [36.6731, 436, 1.6948, 18, 4], + [34.9798, 456, 4.2339, 21, 0, true, 0, true], + [36.6731, 457, 1.6948, 18, 4], + [34.9798, 477, 4.2339, 21, 0, true, 0, true], + [36.6731, 478, 1.6948, 18, 4], + [34.9798, 498, 4.2339, 21, 0, true, 0, true], + [36.6731, 499, 1.6948, 18, 4], + [34.9798, 519, 4.2339, 21, 0, true, 0, true], + [36.6731, 520, 1.6948, 18, 4], + [34.9798, 540, 4.2339, 21, 0, true, 0, true], + [36.6731, 541, 1.6948, 18, 4], + [34.9798, 561, 4.2339, 21, 0, true, 0, true], + [36.6731, 562, 1.6948, 18, 4], + [34.9798, 582, 4.2339, 21, 0, true, 0, true], + [36.6731, 583, 1.6948, 18, 4], + [40.9069, 100, 16.0897, 18, 4], + [40.9069, 121, 45.7283, 18, 4], + [40.9069, 142, 49.1148, 18, 4], + [40.9069, 163, 59.0931, 18, 4], + [40.9069, 184, 36.4132, 18, 4], + [40.9069, 205, 49.9622, 18, 4], + [40.9069, 226, 44.8809, 18, 4], + [40.9069, 268, 53.3565, 18, 4], + [40.9069, 289, 54.2024, 18, 4], + [40.9069, 310, 11.0084, 18, 4], + [40.9069, 352, 11.0115, 18, 4], + [42.6018, 373, 16.9371, 18, 4], + [40.9069, 394, 5.9303, 18, 4], + [42.6018, 415, 16.0928, 18, 4], + [40.9069, 436, 4.237, 18, 4], + [40.9069, 457, 7.6235, 18, 4], + [42.6018, 478, 17.786, 18, 4], + [40.9069, 499, 33.0283, 18, 4], + [40.9069, 520, 10.1657, 18, 4], + [40.9069, 541, 27.1028, 18, 4], + [40.9069, 562, 34.7231, 18, 4], + [40.9069, 583, 8.4709, 18, 4], + [34.879, 612, 64.2137, 230, 4, true, 15, false], + [34.9798, 654, 4.2339, 21, 0, true, 0, true], + [37.5205, 655, 0.8474, 18, 4], + [34.9798, 675, 4.2339, 21, 0, true, 0, true], + [37.5205, 676, 0.8474, 18, 4], + [37.5205, 697, 0.8474, 18, 4], + [34.9798, 717, 4.2339, 21, 0, true, 0, true], + [37.5205, 718, 0.8474, 18, 4], + [34.9798, 738, 4.2339, 21, 0, true, 0, true], + [37.5205, 739, 0.8474, 18, 4], + [34.9798, 759, 4.2339, 21, 0, true, 0, true], + [37.5205, 760, 0.8474, 18, 4], + [34.9798, 780, 4.2339, 21, 0, true, 0, true], + [37.5205, 781, 0.8474, 18, 4], + [34.9798, 801, 4.2339, 21, 0, true, 0, true], + [37.5205, 802, 0.8474, 18, 4], + [34.9798, 822, 4.2339, 20, 0, true, 0, true], + [37.5205, 823, 0.8474, 18, 4], + [40.9069, 655, 36.4148, 18, 4], + [39.2137, 675, 59.7782, 21, 0], + [40.9069, 697, 40.6549, 18, 4], + [42.6018, 718, 32.184, 18, 4], + [42.6018, 739, 13.5506, 18, 4], + [42.6018, 760, 20.3283, 18, 4], + [42.6018, 781, 4.2354, 18, 4], + [40.9069, 802, 35.5705, 18, 4], + [40.9069, 823, 16.9386, 18, 4] + ] + }, + "1440": { + "name": "repo-pulls-pulldiff--skeleton-fixture", + "viewportWidth": 1408, + "width": 1408, + "height": 842, + "bones": [ + [0, 0, 100, 842, 4, true, 15, true], + [0.6392, 38, 2.983, 36, 4], + [1.6335, 99, 1.1364, 16, 0], + [3.3381, 98, 1.4515, 17, 4], + [2.2017, 125, 20.0284, 403, 0, true, 8, false], + [3.4091, 136, 1.1364, 16, 0], + [5.1136, 135, 1.1242, 17, 4], + [3.9773, 162, 18.2528, 292, 0, true, 8, false], + [5.1847, 173, 1.1364, 16, 0], + [6.8892, 172, 5.8361, 17, 4], + [5.7528, 199, 16.4773, 146, 0, true, 8, false], + [6.9602, 210, 1.1364, 16, 0], + [8.6648, 209, 3.6987, 17, 4], + [7.5284, 238, 14.7017, 33, 0, true, 8, false], + [8.7358, 247, 1.1364, 16, 0], + [10.4403, 246, 9.3228, 17, 4], + [6.9602, 284, 1.1364, 16, 0], + [8.6648, 283, 5.7695, 17, 4], + [7.5284, 312, 14.7017, 33, 0, true, 8, false], + [8.7358, 321, 1.1364, 16, 0], + [10.4403, 320, 10.1152, 17, 4], + [5.1847, 358, 1.1364, 16, 0], + [6.8892, 357, 1.4082, 17, 4], + [5.7528, 386, 16.4773, 33, 0, true, 8, false], + [6.9602, 395, 1.1364, 16, 0], + [8.6648, 394, 7.1222, 17, 4], + [5.1847, 430, 1.1364, 16, 0], + [6.8892, 429, 4.187, 17, 4], + [3.4091, 467, 1.1364, 16, 0], + [5.1136, 466, 2.963, 17, 4], + [3.9773, 495, 18.2528, 33, 0, true, 8, false], + [5.1847, 504, 1.1364, 16, 0], + [6.8892, 503, 6.0669, 17, 4], + [23.9347, 1, 0.071, 840, 4], + [25.142, 20, 1.79, 18, 4], + [27.5002, 20, 1.79, 18, 4], + [30.4266, 21, 7.2521, 17, 4], + [78.6144, 10, 7.6938, 38, 4], + [86.8763, 10, 12.4845, 38, 4, true, 15, true], + [86.8763, 11, 6.9169, 36, "4px 0px 0px 4px"], + [93.7223, 11, 5.6385, 36, "0px 4px 4px 0px"], + [24.5739, 57, 74.7869, 547, 4, true, 15, false], + [26.4349, 100, 0.597, 18, 4], + [24.6449, 120, 2.983, 21, 0, true, 0, true], + [26.4349, 121, 0.597, 18, 4], + [24.6449, 141, 2.983, 21, 0, true, 0, true], + [26.4349, 142, 0.597, 18, 4], + [24.6449, 162, 2.983, 21, 0, true, 0, true], + [26.4349, 163, 0.597, 18, 4], + [26.4349, 184, 0.597, 18, 4], + [24.6449, 204, 2.983, 21, 0, true, 0, true], + [26.4349, 205, 0.597, 18, 4], + [26.4349, 226, 0.597, 18, 4], + [26.4349, 247, 0.597, 18, 4], + [24.6449, 267, 2.983, 21, 0, true, 0, true], + [26.4349, 268, 0.597, 18, 4], + [24.6449, 288, 2.983, 21, 0, true, 0, true], + [26.4349, 289, 0.597, 18, 4], + [26.4349, 310, 0.597, 18, 4], + [25.8378, 331, 1.1941, 18, 4], + [24.6449, 351, 2.983, 21, 0, true, 0, true], + [26.4349, 352, 0.597, 18, 4], + [24.6449, 372, 2.983, 21, 0, true, 0, true], + [25.8378, 373, 1.1941, 18, 4], + [24.6449, 393, 2.983, 21, 0, true, 0, true], + [25.8378, 394, 1.1941, 18, 4], + [24.6449, 414, 2.983, 21, 0, true, 0, true], + [25.8378, 415, 1.1941, 18, 4], + [24.6449, 435, 2.983, 21, 0, true, 0, true], + [25.8378, 436, 1.1941, 18, 4], + [24.6449, 456, 2.983, 21, 0, true, 0, true], + [25.8378, 457, 1.1941, 18, 4], + [24.6449, 477, 2.983, 21, 0, true, 0, true], + [25.8378, 478, 1.1941, 18, 4], + [24.6449, 498, 2.983, 21, 0, true, 0, true], + [25.8378, 499, 1.1941, 18, 4], + [24.6449, 519, 2.983, 21, 0, true, 0, true], + [25.8378, 520, 1.1941, 18, 4], + [24.6449, 540, 2.983, 21, 0, true, 0, true], + [25.8378, 541, 1.1941, 18, 4], + [24.6449, 561, 2.983, 21, 0, true, 0, true], + [25.8378, 562, 1.1941, 18, 4], + [24.6449, 582, 2.983, 21, 0, true, 0, true], + [25.8378, 583, 1.1941, 18, 4], + [28.8208, 100, 11.3359, 18, 4], + [28.8208, 121, 32.2177, 18, 4], + [28.8208, 142, 34.6036, 18, 4], + [28.8208, 163, 48.3254, 18, 4], + [28.8208, 184, 25.6547, 18, 4], + [28.8208, 205, 35.2006, 18, 4], + [28.8208, 226, 31.6206, 18, 4], + [28.8208, 268, 37.5921, 18, 4], + [28.8208, 289, 38.188, 18, 4], + [28.8208, 310, 7.7559, 18, 4], + [28.8208, 352, 7.7581, 18, 4], + [30.0149, 373, 11.9329, 18, 4], + [28.8208, 394, 4.1781, 18, 4], + [30.0149, 415, 11.3381, 18, 4], + [28.8208, 436, 2.9852, 18, 4], + [28.8208, 457, 5.3711, 18, 4], + [30.0149, 478, 12.5311, 18, 4], + [28.8208, 499, 23.2699, 18, 4], + [28.8208, 520, 7.1622, 18, 4], + [28.8208, 541, 19.0951, 18, 4], + [28.8208, 562, 24.464, 18, 4], + [28.8208, 583, 5.9681, 18, 4], + [24.5739, 612, 74.7869, 230, 4, true, 15, false], + [24.6449, 654, 2.983, 21, 0, true, 0, true], + [26.4349, 655, 0.597, 18, 4], + [24.6449, 675, 2.983, 21, 0, true, 0, true], + [26.4349, 676, 0.597, 18, 4], + [26.4349, 697, 0.597, 18, 4], + [24.6449, 717, 2.983, 21, 0, true, 0, true], + [26.4349, 718, 0.597, 18, 4], + [24.6449, 738, 2.983, 21, 0, true, 0, true], + [26.4349, 739, 0.597, 18, 4], + [24.6449, 759, 2.983, 21, 0, true, 0, true], + [26.4349, 760, 0.597, 18, 4], + [24.6449, 780, 2.983, 21, 0, true, 0, true], + [26.4349, 781, 0.597, 18, 4], + [24.6449, 801, 2.983, 21, 0, true, 0, true], + [26.4349, 802, 0.597, 18, 4], + [24.6449, 822, 2.983, 20, 0, true, 0, true], + [26.4349, 823, 0.597, 18, 4], + [28.8208, 655, 25.6559, 18, 4], + [27.6278, 675, 71.6619, 21, 0], + [28.8208, 697, 28.6432, 18, 4], + [30.0149, 718, 22.6751, 18, 4], + [30.0149, 739, 9.547, 18, 4], + [30.0149, 760, 14.3222, 18, 4], + [30.0149, 781, 2.9841, 18, 4], + [28.8208, 802, 25.061, 18, 4], + [28.8208, 823, 11.934, 18, 4] ] } }, - "_hash": "76fba288516849c33f54daa0f17eb092" + "_hash": "88d6bc3060dd984f15375dbc03aed85b" } diff --git a/web/src/lib/components/profile/tabs/OverviewTab.stories.svelte b/web/src/lib/components/profile/tabs/OverviewTab.stories.svelte index 1f1cf8a82..68812df43 100644 --- a/web/src/lib/components/profile/tabs/OverviewTab.stories.svelte +++ b/web/src/lib/components/profile/tabs/OverviewTab.stories.svelte @@ -77,11 +77,13 @@ } ], truncated: false - }; const { Story } = defineMeta({ + }; + const { Story } = defineMeta({ title: "Profile/OverviewTab", component: OverviewTab, tags: ["autodocs"], - args: { pinned, activity, handle: "wren.tngl.sh" } }); + args: { pinned, activity, handle: "wren.tngl.sh" } + }); \ No newline at end of file +/> diff --git a/web/src/lib/components/profile/tabs/OverviewTab.svelte b/web/src/lib/components/profile/tabs/OverviewTab.svelte index 32bc86e1e..e593814f3 100644 --- a/web/src/lib/components/profile/tabs/OverviewTab.svelte +++ b/web/src/lib/components/profile/tabs/OverviewTab.svelte @@ -2,7 +2,11 @@ import RepoCard from "$lib/components/repo/RepoCard.svelte"; import Section from "$lib/components/ui/Section.svelte"; import ProfileActivity from "$lib/components/profile/ProfileActivity.svelte"; - import { repoKey, type ProfileActivityData, type RepoCardData } from "$lib/components/profile/types"; + import { + repoKey, + type ProfileActivityData, + type RepoCardData + } from "$lib/components/profile/types"; interface Props { pinned: RepoCardData[]; activity: ProfileActivityData; diff --git a/web/src/lib/components/repo/CommitView.stories.svelte b/web/src/lib/components/repo/CommitView.stories.svelte index 88c8b8b69..d79e861f0 100644 --- a/web/src/lib/components/repo/CommitView.stories.svelte +++ b/web/src/lib/components/repo/CommitView.stories.svelte @@ -1,71 +1,90 @@ diff --git a/web/src/lib/components/repo/CommitView.svelte b/web/src/lib/components/repo/CommitView.svelte index 63ef92ae8..5587fca08 100644 --- a/web/src/lib/components/repo/CommitView.svelte +++ b/web/src/lib/components/repo/CommitView.svelte @@ -1,13 +1,19 @@
- {#if mirror} - - {:else} - - {/if} + `?diff=${style}`} + {downloadUrls} + {patchUrl} + {patch} + stickyTop="viewport" + />
diff --git a/web/src/lib/components/repo/DiffCardChrome.svelte b/web/src/lib/components/repo/DiffCardChrome.svelte deleted file mode 100644 index 88faf6c12..000000000 --- a/web/src/lib/components/repo/DiffCardChrome.svelte +++ /dev/null @@ -1,67 +0,0 @@ - - - - -
- -
-
- {#if open} -
- {#if actions} -
- {@render actions()} -
- {/if} -
-
- -
- {@render body()} -
-
diff --git a/web/src/lib/components/repo/DiffCodeView.svelte b/web/src/lib/components/repo/DiffCodeView.svelte new file mode 100644 index 000000000..dd20cfd82 --- /dev/null +++ b/web/src/lib/components/repo/DiffCodeView.svelte @@ -0,0 +1,308 @@ + + +{#if initError} +
+ {initError} +
+{:else} +
+
+
+{/if} diff --git a/web/src/lib/components/repo/DiffFileCard.stories.svelte b/web/src/lib/components/repo/DiffFileCard.stories.svelte deleted file mode 100644 index fe4324b44..000000000 --- a/web/src/lib/components/repo/DiffFileCard.stories.svelte +++ /dev/null @@ -1,181 +0,0 @@ - - - { - await waitFor(() => expect(shadowText(canvasElement)).toContain("oklch(0.98 0 0)")); - }} -/> - - { - const card = canvasElement.querySelector("details")!; - await expect(card.open).toBe(true); - await userEvent.click(card.querySelector("summary")!); - await expect(card.open).toBe(false); - await userEvent.click(card.querySelector("summary")!); - await expect(card.open).toBe(true); - await expect(canvas.getByText("src/colors.ts")).toBeInTheDocument(); - }} -/> - - { - // both names live in the same flex row, match the joined text - const header = canvasElement.querySelector("summary")!; - await expect(header.textContent).toContain("src/old-name.ts"); - await expect(header.textContent).toContain("src/new-name.ts"); - }} -/> - - { - await expect( - canvas.getByText("This is a binary file and will not be displayed.") - ).toBeInTheDocument(); - }} -/> - - { - await expect(canvas.getByRole("link", { name: /view file/i })).toHaveAttribute( - "href", - "/dawn/tangled/blob/master/src/colors.ts" - ); - }} -/> - - diff --git a/web/src/lib/components/repo/DiffFileCard.svelte b/web/src/lib/components/repo/DiffFileCard.svelte deleted file mode 100644 index ecd48831b..000000000 --- a/web/src/lib/components/repo/DiffFileCard.svelte +++ /dev/null @@ -1,111 +0,0 @@ - - - - {#snippet header()} - -
- {#if file.name.old && file.name.new && file.name.old !== file.name.new} - {file.name.old} - - {file.name.new} - {:else} - {name} - {/if} -
- {/snippet} - - {#snippet actions()} - {#if blameRequest.loading} - Loading blame… - {/if} - {#if blobUrl} - - {/if} - {@render headerActions?.(file)} - {/snippet} - - {#snippet body()} - {#if file.is_binary} - - {:else} - {#if blameRequest.error} - - {/if} - - {/if} - {/snippet} -
diff --git a/web/src/lib/components/repo/DiffFileHeader.svelte b/web/src/lib/components/repo/DiffFileHeader.svelte new file mode 100644 index 000000000..9f9889e7a --- /dev/null +++ b/web/src/lib/components/repo/DiffFileHeader.svelte @@ -0,0 +1,93 @@ + + +
+ {#snippet titleContent()} + {#if empty} + + {:else if open} +
diff --git a/web/src/lib/components/repo/DiffFileTree.stories.svelte b/web/src/lib/components/repo/DiffFileTree.stories.svelte index c76cde24c..6b0eb5638 100644 --- a/web/src/lib/components/repo/DiffFileTree.stories.svelte +++ b/web/src/lib/components/repo/DiffFileTree.stories.svelte @@ -18,7 +18,7 @@ const nestedTree = async ({ canvas, canvasElement, userEvent }: PlayContext) => { await expect(canvas.getByRole("link", { name: "DiffView.svelte" })).toHaveAttribute( "href", - "#file-src/lib/components/repo/DiffView.svelte" + "#file-src%2Flib%2Fcomponents%2Frepo%2FDiffView.svelte" ); await expect(canvas.getByRole("link", { name: "package.json" })).toHaveAttribute( "href", diff --git a/web/src/lib/components/repo/DiffFileTree.svelte b/web/src/lib/components/repo/DiffFileTree.svelte index bc4c7642f..a62303f59 100644 --- a/web/src/lib/components/repo/DiffFileTree.svelte +++ b/web/src/lib/components/repo/DiffFileTree.svelte @@ -60,15 +60,18 @@ {#snippet nodes(list: TreeNode[])} - {#each list as node (node.path)} + {#each list as node (`${node.type}:${node.path}`)} {#if node.type === "dir"} {@const open = !closed[node.path]}
(closed[node.path] = !e.currentTarget.open)}> @@ -96,10 +99,22 @@
{:else} - + + + { + event.preventDefault(); + onSelect(node.path); + } + : undefined} + > + {/if} {/each} {/snippet} diff --git a/web/src/lib/components/repo/DiffStatPill.svelte b/web/src/lib/components/repo/DiffStatPill.svelte index 0618d8c14..4902db2b5 100644 --- a/web/src/lib/components/repo/DiffStatPill.svelte +++ b/web/src/lib/components/repo/DiffStatPill.svelte @@ -33,10 +33,10 @@ - - - - { - await expect(canvas.getByRole("link", { name: "Download patch" })).toHaveAttribute( - "href", - expect.stringContaining(".patch") - ); - await expect(canvas.getByRole("link", { name: "Download diff" })).toHaveAttribute( - "href", - expect.stringContaining(".diff") - ); - // reloads on purpose, the client router would shadow the endpoint with the - // commit page's [ref] route and 404 - for (const link of canvas.getAllByRole("link", { name: /^Download/ })) { - await expect(link).toHaveAttribute("data-sveltekit-reload"); - } - }} -/> - - { - await userEvent.click(canvas.getByTitle("Toggle file list")); - await expect(onToggleFiles).toHaveBeenCalledOnce(); - - await userEvent.click(canvas.getByRole("button", { name: /collapse all/i })); - await expect(onSetAllOpen).toHaveBeenCalledWith(false); - }} -/> - - { - await expect(canvas.getByRole("button", { name: /expand all/i })).toBeInTheDocument(); - }} -/> - - { - await expect(canvas.getByTitle("Unified diff")).toHaveAttribute("href", "?diff=unified"); - await expect(canvas.getByTitle("Split diff")).toHaveAttribute("href", "?diff=split"); - // replacestate navigation, no noscroll means it jumps to top - for (const link of [canvas.getByTitle("Unified diff"), canvas.getByTitle("Split diff")]) { - await expect(link).toHaveAttribute("data-sveltekit-noscroll"); - } - }} -/> - - - - {#snippet center()} - Round #2 - {/snippet} - {#snippet actions()} - review panel - {/snippet} - - diff --git a/web/src/lib/components/repo/DiffTopbar.svelte b/web/src/lib/components/repo/DiffTopbar.svelte deleted file mode 100644 index ac2d4cda9..000000000 --- a/web/src/lib/components/repo/DiffTopbar.svelte +++ /dev/null @@ -1,139 +0,0 @@ - - -
- {#if onToggleFiles} - - - - {/if} - - {#if onSetAllOpen} - - {/if} - - -
diff --git a/web/src/lib/components/repo/DiffView.stories.svelte b/web/src/lib/components/repo/DiffView.stories.svelte deleted file mode 100644 index 9c7d5ff21..000000000 --- a/web/src/lib/components/repo/DiffView.stories.svelte +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - - - - diff --git a/web/src/lib/components/repo/DiffView.svelte b/web/src/lib/components/repo/DiffView.svelte deleted file mode 100644 index 9001342e8..000000000 --- a/web/src/lib/components/repo/DiffView.svelte +++ /dev/null @@ -1,143 +0,0 @@ - - -{#if loading} - -{:else} -
- 0 ? () => (filesOpen = !filesOpen) : undefined} - {allOpen} - bind:blame - showBlame={blameSource !== undefined && rows.some((row) => row.file.name.old !== "")} - onSetAllOpen={setAll} - center={topbarCenter} - actions={topbarActions} - /> - -
- {#if rows.length > 0 && filesOpen} - - {/if} - -
- {#if rows.length === 0} - - {/if} - {#each rows as row (row.key)} - isOpen(row.key), (open) => (openStates[row.key] = open)} - headerActions={fileHeaderActions} - /> - {/each} -
-
-
-{/if} diff --git a/web/src/lib/components/repo/PierreDiff.stories.svelte b/web/src/lib/components/repo/PierreDiff.stories.svelte deleted file mode 100644 index a1646c4db..000000000 --- a/web/src/lib/components/repo/PierreDiff.stories.svelte +++ /dev/null @@ -1,191 +0,0 @@ - - - { - await appviewChrome(ctx); - await expectSplitColumns(false)(ctx); - await separatorNotClipped(ctx); - }} -/> - - { - await expectSplitColumns(true)(ctx); - await separatorNotClipped(ctx); - }} -/> - - { - await waitFor(() => expect(shadowText(canvasElement)).toContain("19 unmodified lines")); - const host = hostsOf(canvasElement)[0]; - const separator = host.shadowRoot?.querySelector("[data-separator='line-info']"); - expect(separator).not.toBeNull(); - expect(separator?.hasAttribute("data-separator-first")).toBe(true); - }} -/> diff --git a/web/src/lib/components/repo/PierreDiff.svelte b/web/src/lib/components/repo/PierreDiff.svelte deleted file mode 100644 index ad0597bcc..000000000 --- a/web/src/lib/components/repo/PierreDiff.svelte +++ /dev/null @@ -1,115 +0,0 @@ - - -
-
- {#each files, index (index)} - - {#if prerenderedHTML[index]} - - - {/if} - - {/each} -
- {#if blame} - - {/if} -
diff --git a/web/src/lib/components/repo/RepoHeader.stories.svelte b/web/src/lib/components/repo/RepoHeader.stories.svelte index a1b174cfa..5a44bc475 100644 --- a/web/src/lib/components/repo/RepoHeader.stories.svelte +++ b/web/src/lib/components/repo/RepoHeader.stories.svelte @@ -5,7 +5,10 @@ import type { RepoInfo } from "$lib/components/repo/types"; type PlayContext = Pick>, "canvas">; - type InteractionPlayContext = Pick>, "canvas" | "userEvent">; + type InteractionPlayContext = Pick< + StoryContext>, + "canvas" | "userEvent" + >; // the star control is the same ButtonGroup signed in or out, so it always // stands exactly as tall as the fork group beside it diff --git a/web/src/lib/components/repo/TopicList.svelte b/web/src/lib/components/repo/TopicList.svelte index b6542a54a..d4e0a221b 100644 --- a/web/src/lib/components/repo/TopicList.svelte +++ b/web/src/lib/components/repo/TopicList.svelte @@ -50,7 +50,9 @@ aria-hidden="true" > {#each topics as topic, i (topic)} - {topic} + {topic} {/each} +{topics.length} more void; + reject: (err: unknown) => void; + /** set for streamed patches, which answer many times before they finish */ + partial?: (files: FileDiffMetadata[]) => void; +} +const pendingTasks = new Map(); + +const DIFF_WORKER_TIMEOUT_MS = 30_000; + +const failPending = (error: Error) => { + for (const [id, task] of pendingTasks) { + pendingTasks.delete(id); + task.reject(error); + } +}; + +const serializeFileContents = (file: FileContents): FileContents => ({ + name: file.name, + contents: typeof file.contents === "string" ? file.contents : "", + ...(file.cacheKey !== undefined ? { cacheKey: file.cacheKey } : {}) +}); + +const diffWorker = (): Worker => { + if (diffWorkerInstance) return diffWorkerInstance; + const worker = new DiffWorker(); + worker.onmessage = (e: MessageEvent) => { + const task = pendingTasks.get(e.data.id); + if (!task) return; + if (task.partial && !e.data.done) { + if (e.data.files) task.partial(e.data.files); + return; + } + pendingTasks.delete(e.data.id); + task.resolve(e.data); + }; + worker.onerror = () => { + failPending(new Error("diff worker failed")); + diffWorkerInstance?.terminate(); + diffWorkerInstance = undefined; + }; + diffWorkerInstance = worker; + return worker; +}; + +const runInDiffWorker = (build: (id: number) => DiffWorkerRequest): Promise => { + const worker = diffWorker(); + const id = nextTaskId++; + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + if (!pendingTasks.delete(id)) return; + reject(new Error("diff worker timed out")); + }, DIFF_WORKER_TIMEOUT_MS); + pendingTasks.set(id, { + resolve: (response) => { + clearTimeout(timer); + resolve(response); + }, + reject: (err) => { + clearTimeout(timer); + reject(err); + } + }); + worker.postMessage(build(id)); + }); +}; + +export const parseDiffInWorker = async ( + oldFile: FileContents, + newFile: FileContents +): Promise => { + const response = await runInDiffWorker((id) => ({ + id, + kind: "file", + oldFile: serializeFileContents(oldFile), + newFile: serializeFileContents(newFile) + })); + if (response.fileDiff) return response.fileDiff; + throw new Error(response.error ?? "diff worker returned no diff"); +}; + +export const streamPatchInWorker = async ( + url: string, + onFiles: (files: FileDiffMetadata[]) => void, + signal?: AbortSignal +): Promise => { + // Fetch on main thread to reuse cache (worker has separate cache). + let body: ReadableStream | undefined; + try { + const response = await fetch(url, { signal }); + if (!response.ok) throw new Error(`patch ${response.status}`); + body = response.body ?? undefined; + } catch (cause) { + // a failed handover is worth one retry from inside the worker, which has + // its own fetch and no preload to match + if (body === undefined && cause instanceof Error && cause.message.startsWith("patch ")) + throw cause; + } + + const worker = diffWorker(); + const id = nextTaskId++; + await new Promise((resolve, reject) => { + pendingTasks.set(id, { + partial: onFiles, + resolve: (response) => { + if (response.error) return reject(new Error(response.error)); + if (response.files?.length) onFiles(response.files); + resolve(); + }, + reject + }); + const request: DiffWorkerRequest = body + ? { id, kind: "stream", body } + : { id, kind: "stream", url }; + worker.postMessage(request, body ? [body] : []); + + // leaving the page stops the parse too: without this the worker keeps + // chewing through megabytes for a component that no longer exists + signal?.addEventListener( + "abort", + () => { + if (!pendingTasks.delete(id)) return; + worker.postMessage({ id, kind: "cancel" } satisfies DiffWorkerRequest); + resolve(); + }, + { once: true } + ); + }); +}; diff --git a/web/src/lib/components/repo/diffWorker.ts b/web/src/lib/components/repo/diffWorker.ts new file mode 100644 index 000000000..4768e42a9 --- /dev/null +++ b/web/src/lib/components/repo/diffWorker.ts @@ -0,0 +1,132 @@ +import { + parseDiffFromFile, + processPatch, + type FileContents, + type FileDiffMetadata +} from "@pierre/diffs"; + +export interface DiffFileRequest { + id: number; + kind: "file"; + oldFile: FileContents; + newFile: FileContents; +} + +export interface DiffStreamRequest { + id: number; + kind: "stream"; + /** transferred from the main thread, which is where the preload lives */ + body?: ReadableStream; + /** fallback for browsers that will not transfer a stream */ + url?: string; +} + +export interface DiffCancelRequest { + id: number; + kind: "cancel"; +} + +export type DiffWorkerRequest = DiffFileRequest | DiffStreamRequest | DiffCancelRequest; + +export interface DiffWorkerResponse { + id: number; + fileDiff?: FileDiffMetadata; + files?: FileDiffMetadata[]; + /** a stream sends many responses; the last one carries done */ + done?: boolean; + error?: string; +} + +// Pierre's worker requires cacheKey to avoid warnings and cache syntax highlighting. +const withCacheKeys = (files: FileDiffMetadata[]): FileDiffMetadata[] => { + for (const file of files) + file.cacheKey ??= `${file.prevObjectId ?? ""}:${file.newObjectId ?? ""}:${file.name}`; + return files; +}; + +const handle = (request: DiffFileRequest): DiffWorkerResponse => ({ + id: request.id, + fileDiff: parseDiffFromFile(request.oldFile, request.newFile) +}); + +// Every file section in a git format-patch starts with FILE_MARK. +const FILE_MARK = "\ndiff --git "; +// enough files to be worth a message, few enough that the first ones paint while +// the rest of a multi-megabyte body is still arriving +const BATCH = 25; + +// a page that navigated away is not owed the rest of its patch +const cancelled = new Set(); + +const streamPatch = async (request: DiffStreamRequest): Promise => { + // a transferred body is already downloading: the document asked for it before + // this worker existed. only fetch here when nothing was handed over + const body = + request.body ?? + (await (async () => { + if (!request.url) throw new Error("patch stream has neither body nor url"); + const response = await fetch(request.url); + if (!response.ok || !response.body) throw new Error(`patch ${response.status}`); + return response.body; + })()); + + const reader = body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + let batch: FileDiffMetadata[] = []; + + const flush = (done: boolean) => { + if (!done && batch.length === 0) return; + self.postMessage({ id: request.id, files: batch, done } satisfies DiffWorkerResponse); + batch = []; + }; + + for (;;) { + if (cancelled.has(request.id)) { + cancelled.delete(request.id); + await reader.cancel().catch(() => undefined); + return; + } + const { value, done } = await reader.read(); + if (value) buffer += decoder.decode(value, { stream: true }); + // skip the mark at index 0: a section always opens with one + for ( + let mark = buffer.indexOf(FILE_MARK, 1); + mark !== -1; + mark = buffer.indexOf(FILE_MARK, 1) + ) { + const section = buffer.slice(0, mark + 1); + buffer = buffer.slice(mark + 1); + batch.push(...withCacheKeys(processPatch(section).files)); + if (batch.length >= BATCH) flush(false); + } + if (done) break; + } + + buffer += decoder.decode(); + if (buffer.trim()) batch.push(...withCacheKeys(processPatch(buffer).files)); + flush(true); +}; + +self.onmessage = (e: MessageEvent) => { + const fail = (error: unknown) => + self.postMessage({ + id: e.data.id, + done: true, + error: error instanceof Error ? error.message : String(error) + } satisfies DiffWorkerResponse); + + try { + if (e.data.kind === "cancel") { + cancelled.add(e.data.id); + return; + } + if (e.data.kind === "stream") { + void streamPatch(e.data).catch(fail); + return; + } + self.postMessage(handle(e.data) satisfies DiffWorkerResponse); + } catch (error) { + fail(error); + } +}; diff --git a/web/src/lib/components/repo/fileAnchor.svelte.ts b/web/src/lib/components/repo/fileAnchor.svelte.ts new file mode 100644 index 000000000..0d49ed151 --- /dev/null +++ b/web/src/lib/components/repo/fileAnchor.svelte.ts @@ -0,0 +1,57 @@ +import { browser } from "$app/environment"; + +const PREFIX = "#file-"; + +export const fileAnchor = (name: string): string => PREFIX + encodeURIComponent(name); + +const fileFromAnchor = (hash: string): string | undefined => + hash.startsWith(PREFIX) ? decodeURIComponent(hash.slice(PREFIX.length)) : undefined; + +const SETTLE_MS = 250; +const ATTEMPTS = 60; + +const SCROLL_KEYS = new Set(["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Space", "Home", "End"]); + +/** + * Re-aligns deep-linked file anchor while streamed hunks settle layout heights. + */ +export const jumpToAnchoredFile = (jump: (name: string) => number | undefined): void => { + let hash = $state(browser ? location.hash : ""); + let landed: string | undefined; + + $effect(() => { + const track = () => { + hash = location.hash; + landed = undefined; + }; + window.addEventListener("hashchange", track); + return () => window.removeEventListener("hashchange", track); + }); + + $effect(() => { + const name = fileFromAnchor(hash); + if (!name || name === landed) return; + + let attempts = 0; + let previous: number | undefined; + const give = () => { + clearInterval(timer); + window.removeEventListener("wheel", give); + window.removeEventListener("keydown", onKey); + }; + const onKey = (event: KeyboardEvent) => { + if (SCROLL_KEYS.has(event.key)) give(); + }; + const settle = () => { + const at = jump(name); + if (at != null && at === previous) landed = name; + previous = at; + if (landed === name || ++attempts > ATTEMPTS) give(); + }; + const timer = setInterval(settle, SETTLE_MS); + settle(); + window.addEventListener("wheel", give, { passive: true }); + window.addEventListener("keydown", onKey); + return give; + }); +}; diff --git a/web/src/lib/components/repo/fileDiff.test.ts b/web/src/lib/components/repo/fileDiff.test.ts index e7ccaf59a..af5c1df1a 100644 --- a/web/src/lib/components/repo/fileDiff.test.ts +++ b/web/src/lib/components/repo/fileDiff.test.ts @@ -1,144 +1,27 @@ -import { processFile, type FileDiffMetadata } from "@pierre/diffs"; import { describe, expect, it } from "vitest"; -import { LineOp, type DiffFile } from "$lib/api/diff"; -import { renderUnifiedDiff } from "$lib/api/rawdiff"; -import { toFileDiffMetadata } from "$lib/components/repo/fileDiff"; +import { reservedRows } from "$lib/components/repo/fileDiff"; -// the mapper must produce exactly what pierre's own parser -// produces from the rendered text! -// -// excluded: type/prevName, the parser -// can only get it from git header lines the renderer doesn't emit (mode too), -// cacheKey, only worker pools use it, and hunkSpecs -const matchesParsedPatch = (file: DiffFile) => { - const parsed = processFile(renderUnifiedDiff({ diff: [file] })); - expect(parsed).toBeDefined(); - const stripKeys = (value: object, keys: string[]): Record => - Object.fromEntries(Object.entries(value).filter(([key]) => !keys.includes(key))); - const strip = (meta: FileDiffMetadata) => ({ - ...stripKeys(meta, ["type", "prevName", "mode", "cacheKey"]), - hunks: meta.hunks.map((hunk) => stripKeys(hunk, ["hunkSpecs"])) +describe("reservedRows", () => { + const file = (lines: number, insertions: number, deletions: number) => ({ + lines, + stat: { insertions, deletions } }); - expect(strip(toFileDiffMetadata(file))).toEqual(strip(parsed!)); -}; -describe("toFileDiffMetadata", () => { - it("maps a modified file", () => { - matchesParsedPatch({ - name: { old: "foo.go", new: "foo.go" }, - text_fragments: [ - { - Comment: "func main()", - OldPosition: 1, - OldLines: 3, - NewPosition: 1, - NewLines: 3, - Lines: [ - { Op: LineOp.Context, Line: "package main\n" }, - { Op: LineOp.Delete, Line: "// old comment\n" }, - { Op: LineOp.Add, Line: "// new comment\n" }, - { Op: LineOp.Context, Line: "func main() {}\n" } - ] - } - ] - }); + it("takes gitmirror's pair count as the split row count", () => { + expect(reservedRows(file(40, 10, 6), true)).toBe(40); }); - it("maps a new file", () => { - matchesParsedPatch({ - name: { old: "", new: "new.go" }, - is_new: true, - text_fragments: [ - { - OldPosition: 0, - OldLines: 0, - NewPosition: 1, - NewLines: 2, - Lines: [ - { Op: LineOp.Add, Line: "package main\n" }, - { Op: LineOp.Add, Line: "func main() {}\n" } - ] - } - ] - }); + it("adds the replaced lines back for unified rows", () => { + expect(reservedRows(file(40, 10, 6), false)).toBe(46); }); - it("maps a deleted file", () => { - matchesParsedPatch({ - name: { old: "old.go", new: "" }, - is_delete: true, - text_fragments: [ - { - OldPosition: 1, - OldLines: 2, - NewPosition: 0, - NewLines: 0, - Lines: [ - { Op: LineOp.Delete, Line: "package main\n" }, - { Op: LineOp.Delete, Line: "func main() {}\n" } - ] - } - ] - }); + it("counts a pure addition the same either way", () => { + expect(reservedRows(file(21, 21, 0), false)).toBe(21); + expect(reservedRows(file(21, 21, 0), true)).toBe(21); }); - it("maps a renamed file with multiple fragments", () => { - matchesParsedPatch({ - name: { old: "old.go", new: "renamed.go" }, - is_rename: true, - text_fragments: [ - { - OldPosition: 1, - OldLines: 2, - NewPosition: 1, - NewLines: 2, - Lines: [ - { Op: LineOp.Context, Line: "package main\n" }, - { Op: LineOp.Delete, Line: "func old() {}\n" }, - { Op: LineOp.Add, Line: "func renamed() {}\n" } - ] - }, - { - Comment: "func init()", - OldPosition: 10, - OldLines: 1, - NewPosition: 10, - NewLines: 1, - Lines: [{ Op: LineOp.Context, Line: "var x = 1\n" }] - } - ] - }); - }); - - it("maps a missing trailing newline", () => { - matchesParsedPatch({ - name: { old: "foo.go", new: "foo.go" }, - text_fragments: [ - { - OldPosition: 1, - OldLines: 2, - NewPosition: 1, - NewLines: 2, - Lines: [ - { Op: LineOp.Delete, Line: "old" }, - { Op: LineOp.Add, Line: "new\n" }, - { Op: LineOp.Context, Line: "tail" } - ] - } - ] - }); - }); - - it("marks the file type from the wire flags", () => { - expect(toFileDiffMetadata({ name: { old: "", new: "n.go" }, is_new: true }).type).toBe( - "new" - ); - expect(toFileDiffMetadata({ name: { old: "o.go", new: "" }, is_delete: true }).type).toBe( - "deleted" - ); - const renamed = toFileDiffMetadata({ name: { old: "o.go", new: "n.go" }, is_rename: true }); - expect(renamed.type).toBe("rename-changed"); - expect(renamed.prevName).toBe("o.go"); - expect(toFileDiffMetadata({ name: { old: "n.go", new: "n.go" } }).prevName).toBeUndefined(); + it("falls back to the stat sum when the line count is unknown", () => { + expect(reservedRows({ stat: { insertions: 10, deletions: 6 } }, true)).toBe(16); + expect(reservedRows({ stat: { insertions: 10, deletions: 6 } }, false)).toBe(22); }); }); diff --git a/web/src/lib/components/repo/fileDiff.ts b/web/src/lib/components/repo/fileDiff.ts index 7d3c71cda..e0969dfea 100644 --- a/web/src/lib/components/repo/fileDiff.ts +++ b/web/src/lib/components/repo/fileDiff.ts @@ -1,146 +1,12 @@ -import type { ChangeContent, ContextContent, FileDiffMetadata, Hunk } from "@pierre/diffs"; -import { LineOp, type DiffFile } from "$lib/api/diff"; - -// old alone collides when one file is deleted and another renamed onto its -// path in the same commit so we need new too -export const diffRowKey = (file: DiffFile): string => `${file.name.old}→${file.name.new}`; - -// pierre parses patch text into FileDiffMetadata, but the wire already hands -// us hunks, so we can skip the text roundtrip by mapping -export const toFileDiffMetadata = (file: DiffFile): FileDiffMetadata => { - const fragments = file.text_fragments ?? []; - - const hunks: Hunk[] = []; - const deletionLines: string[] = []; - const additionLines: string[] = []; - let splitLineCount = 0; - let unifiedLineCount = 0; - let lastHunkEnd = 0; - - for (const fragment of fragments) { - const additionStart = fragment.NewPosition ?? 0; - const additionCount = fragment.NewLines ?? 0; - const deletionStart = fragment.OldPosition ?? 0; - const deletionCount = fragment.OldLines ?? 0; - - const collapsedBefore = Math.max(additionStart - 1 - lastHunkEnd, 0); - lastHunkEnd = additionStart + additionCount - 1; - - const hunk: Hunk = { - collapsedBefore, - additionStart, - additionCount, - additionLines: 0, - additionLineIndex: additionLines.length, - deletionStart, - deletionCount, - deletionLines: 0, - deletionLineIndex: deletionLines.length, - hunkContent: [], - hunkContext: fragment.Comment, - hunkSpecs: `@@ -${deletionStart},${deletionCount} +${additionStart},${additionCount} @@`, - splitLineStart: 0, - splitLineCount: 0, - unifiedLineStart: 0, - unifiedLineCount: 0, - noEOFCRDeletions: false, - noEOFCRAdditions: false - }; - - let current: ContextContent | ChangeContent | null = null; - let lastOp: LineOp | null = null; - for (const { Op: op, Line: raw } of fragment.Lines ?? []) { - if (op === LineOp.Context) { - if (current?.type !== "context") { - current = { - type: "context", - lines: 0, - additionLineIndex: additionLines.length, - deletionLineIndex: deletionLines.length - }; - hunk.hunkContent.push(current); - } - current.lines++; - additionLines.push(raw); - deletionLines.push(raw); - } else { - if (current?.type !== "change") { - current = { - type: "change", - additions: 0, - deletions: 0, - additionLineIndex: additionLines.length, - deletionLineIndex: deletionLines.length - }; - hunk.hunkContent.push(current); - } - if (op === LineOp.Add) { - current.additions++; - hunk.additionLines++; - additionLines.push(raw); - } else { - current.deletions++; - hunk.deletionLines++; - deletionLines.push(raw); - } - } - lastOp = op; - } - - // a line without a trailing newline is the patch's "\ No newline" - // marker, only possible at the end of the file - const last = fragment.Lines?.[fragment.Lines.length - 1]; - if (last && !last.Line.endsWith("\n")) { - if (last.Op === LineOp.Context) { - hunk.noEOFCRAdditions = true; - hunk.noEOFCRDeletions = true; - } else if (last.Op === LineOp.Delete) { - hunk.noEOFCRDeletions = true; - } else { - hunk.noEOFCRAdditions = true; - } - if (lastOp === LineOp.Add || lastOp === LineOp.Context) { - additionLines[additionLines.length - 1] = last.Line; - } - if (lastOp === LineOp.Delete || lastOp === LineOp.Context) { - deletionLines[deletionLines.length - 1] = last.Line; - } - } - - for (const content of hunk.hunkContent) { - if (content.type === "context") { - hunk.splitLineCount += content.lines; - hunk.unifiedLineCount += content.lines; - } else { - hunk.splitLineCount += Math.max(content.additions, content.deletions); - hunk.unifiedLineCount += content.deletions + content.additions; - } - } - hunk.splitLineStart = splitLineCount + collapsedBefore; - hunk.unifiedLineStart = unifiedLineCount + collapsedBefore; - splitLineCount += collapsedBefore + hunk.splitLineCount; - unifiedLineCount += collapsedBefore + hunk.unifiedLineCount; - - hunks.push(hunk); - } - - const type: FileDiffMetadata["type"] = file.is_new - ? "new" - : file.is_delete - ? "deleted" - : file.is_rename - ? "rename-changed" - : "change"; - - return { - name: file.name.new || file.name.old, - prevName: type === "rename-changed" ? file.name.old : undefined, - type, - hunks, - splitLineCount, - unifiedLineCount, - isPartial: true, - deletionLines, - additionLines - }; -}; +import type { FileDiffMetadata } from "@pierre/diffs"; + +export type DiffEntry = FileDiffMetadata | { note: string }; + +export const reservedRows = ( + file: { lines?: number; stat: { insertions: number; deletions: number } }, + split: boolean +): number => + split + ? (file.lines ?? file.stat.insertions + file.stat.deletions) + : (file.lines ?? file.stat.insertions + file.stat.deletions) + + Math.min(file.stat.insertions, file.stat.deletions); diff --git a/web/src/lib/components/repo/pierre.ts b/web/src/lib/components/repo/pierre.ts index 91880b86c..28c0aadc3 100644 --- a/web/src/lib/components/repo/pierre.ts +++ b/web/src/lib/components/repo/pierre.ts @@ -4,10 +4,12 @@ import { getSharedHighlighter, registerCustomCSSVariableTheme, type FileDiffOptions, - type FileOptions + type FileOptions, + type VirtualFileMetrics } from "@pierre/diffs"; import { browser } from "$app/environment"; import diffShadowCSS from "$lib/components/repo/pierreDiff.css?raw"; +import type { WorkerPoolManager } from "@pierre/diffs/worker"; // pierre's two diff layouts, threaded from the ?diff= url param down to the // FileDiff instances @@ -17,7 +19,7 @@ export type DiffStyle = NonNullable["diffStyle"]>; // colors come from --diffs-token-* properties in pierre.css, they flip // with the media query. the name is pinned by app.css's @utility or tailwind // tree-shakes those rules away -export const PIERRE_THEME_NAME = "tangled"; +const PIERRE_THEME_NAME = "tangled"; registerCustomCSSVariableTheme(PIERRE_THEME_NAME, { // pierre.css defines the real tokens on diffs-container, these are just @@ -40,30 +42,14 @@ registerCustomCSSVariableTheme(PIERRE_THEME_NAME, { // pierre skips highlighting lines past 1e3 chars by default. // lockfiles however do not care... -export const PIERRE_TOKENIZE_MAX_LINE_LENGTH = 5000; +const PIERRE_TOKENIZE_MAX_LINE_LENGTH = 5000; -export const PIERRE_BASE_OPTIONS = { +const PIERRE_BASE_OPTIONS = { theme: PIERRE_THEME_NAME, themeType: "system", tokenizeMaxLineLength: PIERRE_TOKENIZE_MAX_LINE_LENGTH } as const; -// shared by the components and the ssr preloads, the prerendered markup is -// only adoptable when both sides build the same options -export const pierreDiffOptions = (style: DiffStyle): FileDiffOptions => ({ - ...PIERRE_BASE_OPTIONS, - diffStyle: style, - diffIndicators: "classic", - lineDiffType: "word-alt", - // "line-info" separators size the bar to span the whole diff. - // "line-info-basic" never gets that, its count text clips in the 44px - // gutter cell - hunkSeparators: "line-info", - disableFileHeader: true, - overflow: "scroll", - unsafeCSS: diffShadowCSS -}); - // pierre's core stylesheet reaches a container two ways: the `diffs-container` // custom element adopts it in its constructor, but bails when the element already // has a shadow root — which an ssr'd `