((resolve) => (release = resolve));
+ return Response.json({
+ output: { items: [], total: 41 },
+ data: {}
+ });
+ }) as typeof fetch;
+ try {
+ await userEvent.click(canvas.getByRole("button", { name: "Page 2" }));
+ await waitFor(() =>
+ expect(
+ canvasElement.querySelectorAll('[aria-hidden="true"] > .relative')
+ ).toHaveLength(11)
+ );
+ await expect(canvas.queryByRole("link", { name: "repo-0" })).toBeNull();
+ release();
+ await waitFor(() =>
+ expect(
+ canvasElement.querySelectorAll('[aria-hidden="true"] > .relative')
+ ).toHaveLength(0)
+ );
+ } finally {
+ window.fetch = originalFetch;
+ }
+ };
+
const { Story } = defineMeta({
title: "Profile/RepoListTab",
component: RepoListTab,
@@ -86,3 +122,20 @@
{/snippet}
+
+
+ {#snippet template(args)}
+
+ {/snippet}
+
+
+
+ {#snippet template(args)}
+
+ {/snippet}
+
diff --git a/web/src/lib/components/profile/tabs/RepoListTab.svelte b/web/src/lib/components/profile/tabs/RepoListTab.svelte
index eae09118..6fc06424 100644
--- a/web/src/lib/components/profile/tabs/RepoListTab.svelte
+++ b/web/src/lib/components/profile/tabs/RepoListTab.svelte
@@ -5,11 +5,10 @@
import { getAuth } from "$lib/auth.svelte";
import { createBobbinClient } from "$lib/api/client";
import RepoCard from "$lib/components/repo/RepoCard.svelte";
- import Section from "$lib/components/ui/Section.svelte";
- import Pagination from "$lib/components/ui/Pagination.svelte";
- import Error from "$lib/components/ui/Error.svelte";
+ import PagedList from "../PagedList.svelte";
import { createOffsetPager, discoveredPageCount, pageCount } from "../pagination.svelte";
import { PROFILE_PAGE_LIMIT, fetchReposPage } from "../pages";
+ import repoRepocardWithStarButton from "$lib/bones/repo-repocard--with-star-button.bones.json";
import { repoKey, type RepoCardData } from "../types";
import Search from "$icon/search";
import X from "$icon/x";
@@ -40,7 +39,8 @@
}).then((p) => p.items);
}
);
- const repos = $derived(pager.items);
+ // search doesn't return a total count
+ const listed = $derived(searchQuery ? undefined : total);
const pages = $derived(
searchQuery
? discoveredPageCount(pager.state.page, pager.hasNext)
@@ -48,8 +48,12 @@
);
-
{/snippet}
- {#each repos as repo (repoKey(repo))}
+ {#snippet row(repo)}
- {/each}
- {#snippet footer()}
- {#if pages > 1}
-
- {/if}
- {#if pager.state.kind === "failed"}
-
- {/if}
{/snippet}
-
+
diff --git a/web/src/lib/components/profile/tabs/StarredTab.svelte b/web/src/lib/components/profile/tabs/StarredTab.svelte
index d034d2c7..95e525b0 100644
--- a/web/src/lib/components/profile/tabs/StarredTab.svelte
+++ b/web/src/lib/components/profile/tabs/StarredTab.svelte
@@ -5,11 +5,10 @@
import { createBobbinClient } from "$lib/api/client";
import RepoCard from "$lib/components/repo/RepoCard.svelte";
import Card from "$lib/components/ui/Card.svelte";
- import Section from "$lib/components/ui/Section.svelte";
- import Pagination from "$lib/components/ui/Pagination.svelte";
- import Error from "$lib/components/ui/Error.svelte";
+ import PagedList from "../PagedList.svelte";
import { createOffsetPager, pageCount } from "../pagination.svelte";
import { PROFILE_PAGE_LIMIT, fetchStarredPage } from "../pages";
+ import repoRepocardWithStarButton from "$lib/bones/repo-repocard--with-star-button.bones.json";
import type { StarData } from "../types";
interface Props {
@@ -34,12 +33,19 @@
}).then((p) => p.items);
}
);
- const stars = $derived(pager.items);
const pages = $derived(pageCount(total, PROFILE_PAGE_LIMIT));
-
- {#each stars as star (star.uri)}
+ star.uri}
+>
+ {#snippet row(star)}
{#if star.kind === "repo"}
{:else}
@@ -52,22 +58,5 @@
{/if}
- {/each}
- {#snippet footer()}
- {#if pages > 1}
-
- {/if}
- {#if pager.state.kind === "failed"}
-
- {/if}
{/snippet}
-
+
diff --git a/web/src/lib/components/profile/tabs/StringListTab.svelte b/web/src/lib/components/profile/tabs/StringListTab.svelte
index 65d65e97..5cbfd6b8 100644
--- a/web/src/lib/components/profile/tabs/StringListTab.svelte
+++ b/web/src/lib/components/profile/tabs/StringListTab.svelte
@@ -3,11 +3,10 @@
import { getAuth } from "$lib/auth.svelte";
import { createBobbinClient } from "$lib/api/client";
import StringCard from "../StringCard.svelte";
- import Section from "$lib/components/ui/Section.svelte";
- import Pagination from "$lib/components/ui/Pagination.svelte";
- import Error from "$lib/components/ui/Error.svelte";
+ import PagedList from "../PagedList.svelte";
import { createOffsetPager, pageCount } from "../pagination.svelte";
import { PROFILE_PAGE_LIMIT, fetchStringsPage } from "../pages";
+ import profileStringcardDefault from "$lib/bones/profile-stringcard--default.bones.json";
import type { StringCardData } from "../types";
interface Props {
@@ -29,29 +28,19 @@
return fetchStringsPage(ctx, { did, handle, offset }).then((p) => p.items);
}
);
- const strings = $derived(pager.items);
const pages = $derived(pageCount(total, PROFILE_PAGE_LIMIT));
-
- {#each strings as entry (entry.rkey)}
+ entry.rkey}
+>
+ {#snippet row(entry)}
- {/each}
- {#snippet footer()}
- {#if pages > 1}
-
- {/if}
- {#if pager.state.kind === "failed"}
-
- {/if}
{/snippet}
-
+
diff --git a/web/src/lib/components/repo/BlobView.stories.svelte b/web/src/lib/components/repo/BlobView.stories.svelte
index 42e0e621..46f50dd2 100644
--- a/web/src/lib/components/repo/BlobView.stories.svelte
+++ b/web/src/lib/components/repo/BlobView.stories.svelte
@@ -14,13 +14,49 @@
when: "2026-07-28T09:00:00Z"
};
+ const codeContents = `import { serve } from "./server";
+import { createRouter, type Router } from "./router";
+import { logger } from "./logger";
+
+interface ServerOptions {
+ port: number;
+ development: boolean;
+}
+
+const configureRoutes = (router: Router) => {
+ router.get("/health", () => ({ status: "ok" }));
+ router.get("/repos/:did", ({ did }) => loadRepo(did));
+ router.get("/repos/:did/records", ({ did, query }) => listRecords(did, query));
+
+ router.post("/repos/:did/index", async ({ did, body }) => {
+ const result = await indexRepo(did, body.cursor);
+ return { indexed: result.records, cursor: result.cursor };
+ });
+};
+
+const options: ServerOptions = {
+ port: Number(process.env.PORT ?? 5555),
+ development: process.env.NODE_ENV !== "production"
+};
+
+const router = createRouter({ logger });
+configureRoutes(router);
+
+serve({
+ ...options,
+ fetch: router.fetch,
+ onError: (error) => logger.error({ error }, "request failed")
+});
+`;
+ const codeLineCount = codeContents.trimEnd().split("\n").length;
+
const codeBlob: RepoBlobView = {
ref: "main",
path: "web/src/main.ts",
kind: "code",
sizeLabel: "1.2 kB",
- lines: 3,
- contents: 'import { serve } from "./server";\n\nserve({ port: 5555 });\n',
+ lines: codeLineCount,
+ contents: codeContents,
renderedHtml: null,
fileTooLarge: false,
defaultView: "code",
@@ -28,6 +64,94 @@
lastCommit
};
+ // skeleton capture needs headings, wrapping prose, and link lists
+ const markdownContents = `# Tangled
+
+https://tangled.org
+
+The next-generation social coding platform.
+
+## Overview
+
+This monorepo contains the majority of Tangled's open source code. Notably, it
+includes:
+
+* the primary [appview](https://tangled.org/tangled.org/core/tree/master/appview) service,
+* [knotserver](https://tangled.org/tangled.org/core/tree/master/knotserver) for git hosting and operations,
+* [spindle](https://tangled.org/tangled.org/core/tree/master/spindle) for continuous integration,
+* [bobbin](https://tangled.org/tangled.org/core/tree/master/bobbin), the API service,
+
+... and more.
+
+## Bugs
+
+Please file any issues about this code or the hosted service on the [issue
+tracker](https://tangled.org/tangled.org/core/issues). You may also use our
+[Discord](https://chat.tangled.org) for the same.
+
+## Security
+
+If you've identified a security issue in Tangled, please email
+[security@tangled.org](mailto:security@tangled.org) with the details!
+
+## Contributing
+
+PRs welcome! But please file a [proposal for bigger
+changes](https://docs.tangled.org/contribution-guide#proposals-for-bigger-changes).
+We have strict [rules for commit
+messages](https://docs.tangled.org/contribution-guide#commit-guidelines) (or
+skim \`git log\`).
+
+We require a Developer Certificate of Origin (DCO) \`Signed-off-by:\` line in
+commits. [Read more
+here](https://docs.tangled.org/contribution-guide#developer-certificate-of-origin-dco).
+
+## Legal
+
+The contents of this repository are licensed under MIT.
+`;
+
+ const markdownBlob: RepoBlobView = {
+ ...codeBlob,
+ path: "readme.md",
+ kind: "markup",
+ sizeLabel: "1.4 kB",
+ lines: markdownContents.trimEnd().split("\n").length,
+ contents: markdownContents,
+ renderedHtml: `Tangled #
+https://tangled.org
+The next-generation social coding platform.
+Overview #
+This monorepo contains the majority of Tangled's open source code. Notably, it
+includes:
+
+... and more.
+Bugs #
+Please file any issues about this code or the hosted service on the issue
+tracker . You may also use our
+Discord for the same.
+Security #
+If you've identified a security issue in Tangled, please email
+security@tangled.org with the details!
+Contributing #
+PRs welcome! But please file a proposal for bigger
+changes .
+We have strict rules for commit
+messages (or
+skim git log).
+We require a Developer Certificate of Origin (DCO) Signed-off-by: line in
+commits. Read more
+here .
+Legal #
+The contents of this repository are licensed under MIT.
`,
+ defaultView: "rendered"
+ };
+
type PlayContext = Pick<
StoryContext>,
"canvas" | "canvasElement" | "userEvent"
@@ -39,11 +163,15 @@
const showsCode = async ({ canvas, canvasElement }: PlayContext) => {
await expect(canvas.getByText("main.ts")).toBeVisible();
- await expect(canvas.getByText("3 lines")).toBeVisible();
+ await expect(canvas.getByText(`${codeLineCount} lines`)).toBeVisible();
await expect(canvas.getByRole("link", { name: "main" })).toBeVisible();
await expect(canvas.getByText("TypeScript")).toBeVisible();
await expect(canvas.getByRole("link", { name: "View raw" })).toBeVisible();
- await waitFor(() => expect(shadowText(canvasElement)).toContain("serve({ port: 5555 })"));
+ await waitFor(() =>
+ expect(shadowText(canvasElement)).toContain(
+ 'onError: (error) => logger.error({ error }, "request failed")'
+ )
+ );
};
const { Story } = defineMeta({
@@ -58,23 +186,19 @@
tangledsocial code collaboration for the at protocol.
",
- defaultView: "rendered"
- }
- }}
+ args={{ blob: markdownBlob }}
play={async ({ canvas, canvasElement, userEvent }: PlayContext) => {
- await expect(canvas.getByRole("heading", { name: "tangled" })).toBeVisible();
+ await expect(canvas.getByRole("heading", { level: 1, name: /Tangled/ })).toBeVisible();
+ await expect(canvas.getByRole("link", { name: "spindle" })).toBeVisible();
await userEvent.click(canvas.getByRole("button", { name: "View code" }));
- await waitFor(() => expect(shadowText(canvasElement)).toContain("# tangled"));
+ await waitFor(() => expect(shadowText(canvasElement)).toContain("## Contributing"));
}}
/>
+
+
+
+
+
{
- await expect(canvas.getByText("Previews are not supported for this file type.")).toBeVisible();
+ await expect(
+ canvas.getByText("Previews are not supported for this file type.")
+ ).toBeVisible();
}}
/>
@@ -150,3 +276,26 @@
);
}}
/>
+
+
diff --git a/web/src/lib/components/repo/BranchTable.stories.svelte b/web/src/lib/components/repo/BranchTable.stories.svelte
index f2a0d7c5..25c8791f 100644
--- a/web/src/lib/components/repo/BranchTable.stories.svelte
+++ b/web/src/lib/components/repo/BranchTable.stories.svelte
@@ -20,6 +20,12 @@
message: "add stories for everything"
}
];
+ const skeletonBranches: BranchSummary[] = Array.from({ length: 100 }, (_, index) => ({
+ ...branches[index % branches.length],
+ name: index === 0 ? "main" : `feature/branch-${index}`,
+ hash: `${String(index).padStart(8, "0")}abcdef0123456789abcdef0123456789`.slice(0, 40),
+ isDefault: index === 0
+ }));
type PlayContext = Pick>, "canvas">;
@@ -50,4 +56,5 @@
+
diff --git a/web/src/lib/components/repo/CommitLogView.stories.svelte b/web/src/lib/components/repo/CommitLogView.stories.svelte
index aba07d08..24ef6c8c 100644
--- a/web/src/lib/components/repo/CommitLogView.stories.svelte
+++ b/web/src/lib/components/repo/CommitLogView.stories.svelte
@@ -15,6 +15,7 @@
when: "2026-07-28T09:00:00Z"
});
const commits = Array.from({ length: 12 }, (_, index) => commit(index + 1));
+ const skeletonCommits = Array.from({ length: 60 }, (_, index) => commit(index + 1));
const onPageChange = fn();
type PlayContext = Pick>, "canvas">;
@@ -89,6 +90,7 @@
pipelineStatuses: Promise.resolve(pipelinesByCommitHash(commits.map((c) => c.hash)))
}}
/>
+
diff --git a/web/src/lib/components/repo/CommitView.stories.svelte b/web/src/lib/components/repo/CommitView.stories.svelte
index 2881a668..3d2e2cb9 100644
--- a/web/src/lib/components/repo/CommitView.stories.svelte
+++ b/web/src/lib/components/repo/CommitView.stories.svelte
@@ -15,23 +15,47 @@
"add a storybook fixture\n\nthis body is hidden until the disclosure button is pressed.\n\nCo-authored-by: riley ",
change_id: "kqpuwoxzrnvs"
},
- stat: { insertions: 2, deletions: 1, files_changed: 2 },
+ stat: { insertions: 6, deletions: 3, files_changed: 2 },
diff: [
{
name: { old: "src/colors.ts", new: "src/colors.ts" },
text_fragments: [
{
OldPosition: 1,
- OldLines: 3,
+ OldLines: 23,
NewPosition: 1,
- NewLines: 4,
- LinesAdded: 2,
- LinesDeleted: 1,
+ NewLines: 26,
+ LinesAdded: 6,
+ LinesDeleted: 3,
Lines: [
- { Op: 0, Line: "export const palette = {\n" },
+ { Op: 0, Line: "export interface Palette {\n" },
+ { Op: 0, Line: "\tbackground: string;\n" },
+ { Op: 0, Line: "\tforeground: string;\n" },
+ { Op: 0, Line: "\tborder: string;\n" },
+ { Op: 0, Line: "\taccent: string;\n" },
+ { Op: 0, Line: "}\n" },
+ { Op: 0, Line: "\n" },
+ { Op: 0, Line: "export const palette: Palette = {\n" },
{ Op: 1, Line: '\tbackground: "white",\n' },
{ Op: 2, Line: '\tbackground: "oklch(0.98 0 0)",\n' },
+ { Op: 1, Line: '\tforeground: "black",\n' },
{ Op: 2, Line: '\tforeground: "oklch(0.2 0 0)",\n' },
+ { Op: 0, Line: '\tborder: "oklch(0.88 0 0)",\n' },
+ { Op: 2, Line: '\taccent: "oklch(0.62 0.18 250)",\n' },
+ { Op: 0, Line: "};\n" },
+ { Op: 0, Line: "\n" },
+ { Op: 0, Line: "export const syntax = {\n" },
+ { Op: 1, Line: '\tcomment: "#777",\n' },
+ { Op: 2, Line: '\tcomment: "oklch(0.55 0 0)",\n' },
+ { Op: 2, Line: '\tkeyword: "oklch(0.52 0.2 285)",\n' },
+ { Op: 0, Line: '\tstring: "oklch(0.58 0.16 145)",\n' },
+ { Op: 0, Line: '\tnumber: "oklch(0.6 0.15 55)",\n' },
+ { Op: 0, Line: "};\n" },
+ { Op: 0, Line: "\n" },
+ { Op: 0, Line: "export const theme = {\n" },
+ { Op: 0, Line: "\tpalette,\n" },
+ { Op: 0, Line: "\tsyntax,\n" },
+ { Op: 2, Line: "\tdark: false\n" },
{ Op: 0, Line: "};\n" }
]
}
diff --git a/web/src/lib/components/repo/RepoHeader.svelte b/web/src/lib/components/repo/RepoHeader.svelte
index 9052db81..28fc4267 100644
--- a/web/src/lib/components/repo/RepoHeader.svelte
+++ b/web/src/lib/components/repo/RepoHeader.svelte
@@ -7,6 +7,8 @@
import ButtonGroup from "$lib/components/ui/ButtonGroup.svelte";
import Tag from "$lib/components/ui/Tag.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import uiLoadingprimitivesCount from "$lib/bones/ui-loadingprimitives--count.bones.json";
import StarButton from "./StarButton.svelte";
import type { RepoCounts, RepoInfo, RepoSource } from "./types";
import { formatCount } from "$lib/format";
@@ -53,6 +55,12 @@
>
{#if typeof counts === "object" && counts !== null && "then" in counts}
+ {#snippet skeleton()}
+
+ {/snippet}
{formatCount((await counts).forks)}
{:else}
diff --git a/web/src/lib/components/repo/RepoIndexView.stories.svelte b/web/src/lib/components/repo/RepoIndexView.stories.svelte
index 74034908..60839766 100644
--- a/web/src/lib/components/repo/RepoIndexView.stories.svelte
+++ b/web/src/lib/components/repo/RepoIndexView.stories.svelte
@@ -95,6 +95,57 @@
totalTags: 100,
refs: { ...content.refs, capped: true }
});
+ const skeletonFiles: IndexContent["files"] = [
+ { name: "appview", kind: "directory", size: 0 },
+ { name: "bobbin", kind: "directory", size: 0 },
+ { name: "cmd", kind: "directory", size: 0 },
+ { name: "docs", kind: "directory", size: 0 },
+ { name: "web", kind: "directory", size: 0 },
+ { name: "AGENTS.md", kind: "file", size: 1842 },
+ { name: "LICENSE", kind: "file", size: 1067 },
+ { name: "README.md", kind: "file", size: 4280 }
+ ];
+ const skeletonCommits = Array.from({ length: 5 }, (_, index) => {
+ const commit = commits[index % commits.length]!;
+ const prefix = String(index).repeat(8);
+ return { ...commit, hash: `${prefix}${commit.hash}`.slice(0, 40), shortHash: prefix };
+ });
+ const skeletonBranches: IndexContent["branches"] = ["main", "next", "feature/skeletons"].map(
+ (name, index) => ({
+ name,
+ hash: skeletonCommits[index]!.hash,
+ when: skeletonCommits[index]!.when,
+ isDefault: index === 0
+ })
+ );
+ const skeletonTags: IndexContent["tags"] = ["v1.0.0", "v0.9.0", "v0.8.0"].map(
+ (name, index) => ({
+ name,
+ hash: skeletonCommits[index]!.hash,
+ commitHash: skeletonCommits[index]!.hash
+ })
+ );
+ const skeletonReadme = `
+ Tangled
+ A social-enabled git collaboration platform built on atproto. Tangled connects repositories, issues, pull requests, and continuous integration without giving one forge control of the network.
+ Development
+ The web client, appview, knot, spindle, and bobbin live together so protocol and interface changes can be tested as one system.
+ `;
+ const skeletonData = indexData(
+ {
+ files: skeletonFiles,
+ commits: skeletonCommits,
+ branches: skeletonBranches,
+ totalBranches: skeletonBranches.length,
+ tags: skeletonTags,
+ totalTags: skeletonTags.length
+ },
+ { readmeHtml: Promise.resolve(skeletonReadme) }
+ );
+ const panelSkeletonData = indexData(
+ { files: skeletonFiles, readme: null },
+ { readmeHtml: Promise.resolve(null) }
+ );
const emptyData = indexData(
{
availability: "empty",
@@ -132,6 +183,8 @@
+
+
diff --git a/web/src/lib/components/repo/RepoIndexView.svelte b/web/src/lib/components/repo/RepoIndexView.svelte
index bddbc0ad..3a4c9079 100644
--- a/web/src/lib/components/repo/RepoIndexView.svelte
+++ b/web/src/lib/components/repo/RepoIndexView.svelte
@@ -10,6 +10,9 @@
import LanguageBar from "./LanguageBar.svelte";
import PanelHeader from "./PanelHeader.svelte";
import Readme from "./Readme.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoReadmeRenderedSanitizedHtml from "$lib/bones/repo-readme--rendered-sanitized-html.bones.json";
+ import repoRepoindexviewPanelSkeletonFixture from "$lib/bones/repo-repoindexview--panel-skeleton-fixture.bones.json";
import RepoToolbar from "./RepoToolbar.svelte";
import TagList from "./TagList.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
@@ -31,6 +34,10 @@
+ {#snippet skeleton()}
+
+ {/snippet}
+
{@const content = await data.content}
{#if content.availability === "needs-upgrade"}
@@ -147,6 +154,10 @@
+ {#snippet skeleton()}
+
+ {/snippet}
+
{@const readme = (await data.content).readme}
{#if readme}
{starred ? "Unstar" : "Star"}
- {#if loadedCount !== undefined}
+ {#if loadedCount === undefined}
+
+ {:else}
{formatCount(starCount.value)}
{/if}
diff --git a/web/src/lib/components/repo/StarredByList.stories.svelte b/web/src/lib/components/repo/StarredByList.stories.svelte
new file mode 100644
index 00000000..03187034
--- /dev/null
+++ b/web/src/lib/components/repo/StarredByList.stories.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+ {#each starrers as starrer (starrer.did)}
+
+ {/each}
+
+
diff --git a/web/src/lib/components/repo/TagList.stories.svelte b/web/src/lib/components/repo/TagList.stories.svelte
index 0aad46a4..03e0d27c 100644
--- a/web/src/lib/components/repo/TagList.stories.svelte
+++ b/web/src/lib/components/repo/TagList.stories.svelte
@@ -1,6 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/repo/issues/IssueThreadView.stories.svelte b/web/src/lib/components/repo/issues/IssueThreadView.stories.svelte
new file mode 100644
index 00000000..3cd1c4da
--- /dev/null
+++ b/web/src/lib/components/repo/issues/IssueThreadView.stories.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
diff --git a/web/src/lib/components/repo/issues/IssueToolbar.svelte b/web/src/lib/components/repo/issues/IssueToolbar.svelte
index 6107ab94..725bd420 100644
--- a/web/src/lib/components/repo/issues/IssueToolbar.svelte
+++ b/web/src/lib/components/repo/issues/IssueToolbar.svelte
@@ -7,6 +7,8 @@
import ButtonGroup, { segmentProps } from "$lib/components/ui/ButtonGroup.svelte";
import IssueSearch from "./IssueSearch.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import uiLoadingprimitivesCount from "$lib/bones/ui-loadingprimitives--count.bones.json";
import { formatCount } from "$lib/format";
interface Props {
@@ -72,6 +74,9 @@
{#snippet pendingCount(value: number | Promise)}
+ {#snippet skeleton()}
+
+ {/snippet}
{formatCount(await value)}
{/snippet}
diff --git a/web/src/lib/components/repo/pipelines/PipelineCardContent.svelte b/web/src/lib/components/repo/pipelines/PipelineCardContent.svelte
index ffb7233a..ff1307c4 100644
--- a/web/src/lib/components/repo/pipelines/PipelineCardContent.svelte
+++ b/web/src/lib/components/repo/pipelines/PipelineCardContent.svelte
@@ -47,8 +47,9 @@
import { longSummary, totalDuration } from "./pipeline";
import PipelineWorkflows from "./PipelineWorkflows.svelte";
import Separator from "$lib/components/ui/Separator.svelte";
- import Skeleton from "$lib/components/ui/Skeleton.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import uiLoadingprimitivesShortText from "$lib/bones/ui-loadingprimitives--short-text.bones.json";
import Button from "$lib/components/ui/Button.svelte";
interface Props {
@@ -94,7 +95,7 @@
{@const sourceLabel = pipeline.trigger.sourceLabel}
{#snippet skeleton()}
-
+
{/snippet}
{#snippet failed()}
{sourceLabel}
diff --git a/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte b/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte
index 02b1daeb..00930ae0 100644
--- a/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte
+++ b/web/src/lib/components/repo/pipelines/PipelineToolbar.svelte
@@ -23,6 +23,8 @@
import Button from "$lib/components/ui/Button.svelte";
import ButtonGroup, { segmentProps } from "$lib/components/ui/ButtonGroup.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import uiLoadingprimitivesCount from "$lib/bones/ui-loadingprimitives--count.bones.json";
import type { PipelineFilter } from "./pipeline";
import PipelineSearch from "./PipelineSearch.svelte";
import { formatCount } from "$lib/format";
@@ -88,6 +90,13 @@
{#snippet pendingCount(value: number | Promise)}
+ {#snippet skeleton()}
+
+
+
+ pipeline runs
+
+ {/snippet}
{@const count = await value}
{#if count > 0}
diff --git a/web/src/lib/components/repo/pipelines/WorkflowRunView.stories.svelte b/web/src/lib/components/repo/pipelines/WorkflowRunView.stories.svelte
new file mode 100644
index 00000000..23e13d39
--- /dev/null
+++ b/web/src/lib/components/repo/pipelines/WorkflowRunView.stories.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/web/src/lib/components/repo/pulls/PullList.stories.svelte b/web/src/lib/components/repo/pulls/PullList.stories.svelte
new file mode 100644
index 00000000..350b29a9
--- /dev/null
+++ b/web/src/lib/components/repo/pulls/PullList.stories.svelte
@@ -0,0 +1,27 @@
+
+
+
diff --git a/web/src/lib/components/repo/pulls/PullToolbar.stories.svelte b/web/src/lib/components/repo/pulls/PullToolbar.stories.svelte
new file mode 100644
index 00000000..790bff59
--- /dev/null
+++ b/web/src/lib/components/repo/pulls/PullToolbar.stories.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/web/src/lib/components/repo/pulls/PullToolbar.svelte b/web/src/lib/components/repo/pulls/PullToolbar.svelte
index 9bb09f01..995abbf2 100644
--- a/web/src/lib/components/repo/pulls/PullToolbar.svelte
+++ b/web/src/lib/components/repo/pulls/PullToolbar.svelte
@@ -8,6 +8,8 @@
import ButtonGroup, { segmentProps } from "$lib/components/ui/ButtonGroup.svelte";
import PullSearch from "./PullSearch.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import uiLoadingprimitivesCount from "$lib/bones/ui-loadingprimitives--count.bones.json";
import { formatCount } from "$lib/format";
interface Props {
@@ -83,6 +85,9 @@
{#snippet pendingCount(value: number | Promise)}
+ {#snippet skeleton()}
+
+ {/snippet}
{formatCount(await value)}
{/snippet}
diff --git a/web/src/lib/components/settings/SettingsPanel.stories.svelte b/web/src/lib/components/settings/SettingsPanel.stories.svelte
index 06ae4b36..6f709b03 100644
--- a/web/src/lib/components/settings/SettingsPanel.stories.svelte
+++ b/web/src/lib/components/settings/SettingsPanel.stories.svelte
@@ -128,3 +128,71 @@
+
+
+
+
+
+ {#snippet action()}
+ Add collaborator
+ {/snippet}
+
+
+ {#snippet label()}
+
+ user.tld
+ Owner
+
+ {/snippet}
+
+
+ {#snippet label()}
+
+ user2.tld
+ Collaborator
+
+ {/snippet}
+ Remove collaborator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ did:plc:repo
+
+
+
+ {#snippet action()}
+ Delete repository
+ {/snippet}
+
+
+
diff --git a/web/src/lib/components/settings/tabs/KeysTab.svelte b/web/src/lib/components/settings/tabs/KeysTab.svelte
index c5a7e6a6..6bb37ec1 100644
--- a/web/src/lib/components/settings/tabs/KeysTab.svelte
+++ b/web/src/lib/components/settings/tabs/KeysTab.svelte
@@ -9,7 +9,8 @@
type PublicKeyRecord
} from "$lib/api/settings";
import Button from "$lib/components/ui/Button.svelte";
- import Skeleton from "$lib/components/ui/Skeleton.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import ErrorAlert from "$lib/components/ui/Error.svelte";
import SettingsEmpty from "../SettingsEmpty.svelte";
import TimeAgo from "$lib/components/ui/TimeAgo.svelte";
@@ -79,21 +80,7 @@
{#if loaded.loading}
-
-
- {#each [0, 1] as row (row)}
-
- {/each}
-
+
{:else if loaded.data?.keys.length === 0}
{:else if loaded.data}
diff --git a/web/src/lib/components/settings/tabs/NotificationsTab.svelte b/web/src/lib/components/settings/tabs/NotificationsTab.svelte
index 0904d42b..b5eb3a13 100644
--- a/web/src/lib/components/settings/tabs/NotificationsTab.svelte
+++ b/web/src/lib/components/settings/tabs/NotificationsTab.svelte
@@ -11,7 +11,8 @@
} from "$lib/api/notifications";
import Button from "$lib/components/ui/Button.svelte";
import Toggle from "$lib/components/ui/Toggle.svelte";
- import Skeleton from "$lib/components/ui/Skeleton.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import uiToggleChecked from "$lib/bones/ui-toggle--checked.bones.json";
import ErrorAlert from "$lib/components/ui/Error.svelte";
import SettingsHeader from "../SettingsHeader.svelte";
import SettingsList from "../SettingsList.svelte";
@@ -168,14 +169,22 @@
- {pref.label}
+ {pref.label}
- {pref.description}
+ {pref.description}
{#if loaded.loading}
-
+
{:else}
-
+
{/if}
{/each}
diff --git a/web/src/lib/components/settings/tabs/SitesTab.svelte b/web/src/lib/components/settings/tabs/SitesTab.svelte
index ca0b8509..2259f4c0 100644
--- a/web/src/lib/components/settings/tabs/SitesTab.svelte
+++ b/web/src/lib/components/settings/tabs/SitesTab.svelte
@@ -8,7 +8,8 @@
import Input from "$lib/components/ui/Input.svelte";
import Tag from "$lib/components/ui/Tag.svelte";
import ErrorAlert from "$lib/components/ui/Error.svelte";
- import Skeleton from "$lib/components/ui/Skeleton.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import SettingsEmpty from "../SettingsEmpty.svelte";
import SettingsHeader from "../SettingsHeader.svelte";
import SettingsToolbar from "../SettingsToolbar.svelte";
@@ -101,13 +102,7 @@
{#if loaded.loading}
-
+
{:else if loaded.data}
diff --git a/web/src/lib/components/ui/Bones.svelte b/web/src/lib/components/ui/Bones.svelte
new file mode 100644
index 00000000..4f190fa8
--- /dev/null
+++ b/web/src/lib/components/ui/Bones.svelte
@@ -0,0 +1,87 @@
+
+
+
+
+{#if shot}
+
+ {#each Array(repeat).keys() as index (index)}
+
+ {#each shot.bones as bone, i (i)}
+
+ {/each}
+
+ {/each}
+
+{/if}
diff --git a/web/src/lib/components/ui/Delayed.svelte b/web/src/lib/components/ui/Delayed.svelte
new file mode 100644
index 00000000..581cbda2
--- /dev/null
+++ b/web/src/lib/components/ui/Delayed.svelte
@@ -0,0 +1,24 @@
+
+
+{#if show}
+ {@render children()}
+{/if}
diff --git a/web/src/lib/components/ui/LoadingPrimitives.stories.svelte b/web/src/lib/components/ui/LoadingPrimitives.stories.svelte
new file mode 100644
index 00000000..cfb9c92f
--- /dev/null
+++ b/web/src/lib/components/ui/LoadingPrimitives.stories.svelte
@@ -0,0 +1,16 @@
+
+
+
+ 888
+
+
+
+ repository label
+
diff --git a/web/src/lib/components/ui/Pending.svelte b/web/src/lib/components/ui/Pending.svelte
index 7a388055..0a260ade 100644
--- a/web/src/lib/components/ui/Pending.svelte
+++ b/web/src/lib/components/ui/Pending.svelte
@@ -1,26 +1,41 @@
-
+
+
{#if failedProp}
-
+
{@render children()}
{#snippet failed(error: unknown)}
{@render failedProp(error)}
{/snippet}
{:else}
-
+
{@render children()}
{/if}
+
+
+{#snippet gated()}
+ {#if skeleton}
+
+ {@render skeleton()}
+
+ {/if}
+{/snippet}
diff --git a/web/src/lib/components/ui/Skeleton.svelte b/web/src/lib/components/ui/Skeleton.svelte
deleted file mode 100644
index 9cce673f..00000000
--- a/web/src/lib/components/ui/Skeleton.svelte
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
diff --git a/web/src/lib/components/ui/Tabs.stories.svelte b/web/src/lib/components/ui/Tabs.stories.svelte
index d83a3b39..e1706085 100644
--- a/web/src/lib/components/ui/Tabs.stories.svelte
+++ b/web/src/lib/components/ui/Tabs.stories.svelte
@@ -1,6 +1,6 @@
+{#snippet peopleSkeleton(title: string)}
+
+{/snippet}
+
{#if data.tab === "overview"}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const identity = await data.identity}
{:else if data.tab === "repos"}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const identity = await data.identity}
{@const counts = await data.counts}
{:else if data.tab === "starred"}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const identity = await data.identity}
{@const counts = await data.counts}
{:else if data.tab === "strings"}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const identity = await data.identity}
{@const counts = await data.counts}
{:else if data.tab === "followers"}
+ {#snippet skeleton()}
+ {@render peopleSkeleton("Followers")}
+ {/snippet}
{@const identity = await data.identity}
{@const counts = await data.counts}
{:else if data.tab === "following"}
+ {#snippet skeleton()}
+ {@render peopleSkeleton("Following")}
+ {/snippet}
{@const identity = await data.identity}
{@const counts = await data.counts}
{:else if data.tab === "vouches"}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const identity = await data.identity}
diff --git a/web/src/routes/[handle]/[repo]/+layout@.svelte b/web/src/routes/[handle]/[repo]/+layout@.svelte
index d7ab6dcb..b5bdb6f3 100644
--- a/web/src/routes/[handle]/[repo]/+layout@.svelte
+++ b/web/src/routes/[handle]/[repo]/+layout@.svelte
@@ -4,6 +4,8 @@
import { goto } from "$app/navigation";
import { page } from "$app/state";
import RepoChrome from "$lib/components/repo/RepoChrome.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoRepoheaderFullMetadata from "$lib/bones/repo-repoheader--full-metadata.bones.json";
import Error from "$lib/components/ui/Error.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
@@ -111,6 +113,9 @@
{#key `${page.params.handle}/${page.params.repo}`}
+ {#snippet skeleton()}
+
+ {/snippet}
{#snippet failed(_error: unknown)}
diff --git a/web/src/routes/[handle]/[repo]/+page.svelte b/web/src/routes/[handle]/[repo]/+page.svelte
index 46960702..7db64d9d 100644
--- a/web/src/routes/[handle]/[repo]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/+page.svelte
@@ -1,5 +1,7 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
diff --git a/web/src/routes/[handle]/[repo]/blob/[ref]/[...path]/+page.svelte b/web/src/routes/[handle]/[repo]/blob/[ref]/[...path]/+page.svelte
index a360853b..85c0a896 100644
--- a/web/src/routes/[handle]/[repo]/blob/[ref]/[...path]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/blob/[ref]/[...path]/+page.svelte
@@ -1,4 +1,8 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const blob = await data.blob}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const commitDiff = await data.commitDiff}
{@const wire = commitDiff.commit ?? {}}
diff --git a/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte b/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte
index 37c483e5..86dcb281 100644
--- a/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte
@@ -2,6 +2,8 @@
import { goto } from "$app/navigation";
import { resolve } from "$app/paths";
import { commitStatuses } from "$lib/api/commitStatuses";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoCommitlogviewSkeletonFixture from "$lib/bones/repo-commitlogview--skeleton-fixture.bones.json";
import CommitLogView from "$lib/components/repo/CommitLogView.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
@@ -19,6 +21,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const commits = await data.commits}
+ {#snippet skeleton()}
+
+
+
+
+
+
+ {/snippet}
{@const repo = await data.repo}
@@ -22,6 +33,13 @@
+ {#snippet skeleton()}
+
+ {/snippet}
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoIssuesIssuethreadviewSkeletonFixture from "$lib/bones/repo-issues-issuethreadview--skeleton-fixture.bones.json";
import IssueThreadView from "$lib/components/repo/issues/IssueThreadView.svelte";
import EmptyState from "$lib/components/ui/EmptyState.svelte";
import ErrorAlert from "$lib/components/ui/Error.svelte";
@@ -8,6 +10,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const result = await data.page}
{#if result.kind === "ok"}
diff --git a/web/src/routes/[handle]/[repo]/issues/new/+page.svelte b/web/src/routes/[handle]/[repo]/issues/new/+page.svelte
index a89cbf4c..d6696efa 100644
--- a/web/src/routes/[handle]/[repo]/issues/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/issues/new/+page.svelte
@@ -4,6 +4,8 @@
import { resolve } from "$app/paths";
import IssueForm from "$lib/components/repo/issues/IssueForm.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoIssuesIssueformCreate from "$lib/bones/repo-issues-issueform--create.bones.json";
import TabPanel from "$lib/components/ui/TabPanel.svelte";
let { data } = $props();
@@ -40,6 +42,9 @@
Create a new issue
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const markup = {
repo: `${repo.ownerHandle}/${repo.name}`,
diff --git a/web/src/routes/[handle]/[repo]/pipelines/+page.svelte b/web/src/routes/[handle]/[repo]/pipelines/+page.svelte
index 85a1f880..0a5b1b28 100644
--- a/web/src/routes/[handle]/[repo]/pipelines/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/pipelines/+page.svelte
@@ -2,6 +2,9 @@
import PipelineList from "$lib/components/repo/pipelines/PipelineList.svelte";
import PipelineToolbar from "$lib/components/repo/pipelines/PipelineToolbar.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoPipelinesPipelinelistDefault from "$lib/bones/repo-pipelines-pipelinelist--default.bones.json";
+ import repoPipelinesPipelinetoolbarAll from "$lib/bones/repo-pipelines-pipelinetoolbar--all.bones.json";
import TabPanel from "$lib/components/ui/TabPanel.svelte";
import Error from "$lib/components/ui/Error.svelte";
@@ -9,6 +12,14 @@
+ {#snippet skeleton()}
+
+
+ {/snippet}
{@const repo = await data.repo}
@@ -23,6 +34,13 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const spindleError = await data.spindleError}
{#if spindleError}
diff --git a/web/src/routes/[handle]/[repo]/pipelines/[pipeline]/workflow/[workflow]/+page.svelte b/web/src/routes/[handle]/[repo]/pipelines/[pipeline]/workflow/[workflow]/+page.svelte
index 1a04777a..35c3608d 100644
--- a/web/src/routes/[handle]/[repo]/pipelines/[pipeline]/workflow/[workflow]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/pipelines/[pipeline]/workflow/[workflow]/+page.svelte
@@ -2,6 +2,8 @@
import { page } from "$app/state";
import WorkflowRunView from "$lib/components/repo/pipelines/WorkflowRunView.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoPipelinesWorkflowrunviewSkeletonFixture from "$lib/bones/repo-pipelines-workflowrunview--skeleton-fixture.bones.json";
let { data } = $props();
@@ -11,6 +13,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const pipeline = await data.pipeline}
{@const workflow = await data.workflow}
diff --git a/web/src/routes/[handle]/[repo]/pulls/+page.svelte b/web/src/routes/[handle]/[repo]/pulls/+page.svelte
index 10e3d12d..c9c81998 100644
--- a/web/src/routes/[handle]/[repo]/pulls/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/pulls/+page.svelte
@@ -2,12 +2,23 @@
import PullList from "$lib/components/repo/pulls/PullList.svelte";
import PullToolbar from "$lib/components/repo/pulls/PullToolbar.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoPullsPulllistSkeletonFixture from "$lib/bones/repo-pulls-pulllist--skeleton-fixture.bones.json";
+ import repoPullsPulltoolbarSkeletonFixture from "$lib/bones/repo-pulls-pulltoolbar--skeleton-fixture.bones.json";
import TabPanel from "$lib/components/ui/TabPanel.svelte";
let { data } = $props();
+ {#snippet skeleton()}
+
+
+
+
+
+
+ {/snippet}
{@const repo = await data.repo}
@@ -23,6 +34,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const compose = await data.compose}
{@const markup = {
diff --git a/web/src/routes/[handle]/[repo]/settings/+layout.svelte b/web/src/routes/[handle]/[repo]/settings/+layout.svelte
index b108de93..e2562ade 100644
--- a/web/src/routes/[handle]/[repo]/settings/+layout.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/+layout.svelte
@@ -1,6 +1,8 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const base = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/+page.svelte b/web/src/routes/[handle]/[repo]/settings/+page.svelte
index 40df4877..2c317e8f 100644
--- a/web/src/routes/[handle]/[repo]/settings/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/+page.svelte
@@ -5,6 +5,8 @@
import type { InputTag } from "$lib/components/ui/inputField";
import Pending from "$lib/components/ui/Pending.svelte";
import Select from "$lib/components/ui/Select.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import Toggle from "$lib/components/ui/Toggle.svelte";
import SettingsEmpty from "$lib/components/settings/SettingsEmpty.svelte";
import SettingsSection from "$lib/components/settings/SettingsSection.svelte";
@@ -83,6 +85,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const base = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/access/+page.svelte b/web/src/routes/[handle]/[repo]/settings/access/+page.svelte
index 7033864e..46bf5b2c 100644
--- a/web/src/routes/[handle]/[repo]/settings/access/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/access/+page.svelte
@@ -3,6 +3,8 @@
import Avatar from "$lib/components/ui/Avatar.svelte";
import Button from "$lib/components/ui/Button.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import SettingsBlock from "$lib/components/settings/SettingsBlock.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import SettingsRow from "$lib/components/settings/SettingsRow.svelte";
@@ -39,6 +41,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const base = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/access/new/+page.svelte b/web/src/routes/[handle]/[repo]/settings/access/new/+page.svelte
index b5dcda05..95690b14 100644
--- a/web/src/routes/[handle]/[repo]/settings/access/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/access/new/+page.svelte
@@ -4,6 +4,8 @@
import Button from "$lib/components/ui/Button.svelte";
import Input from "$lib/components/ui/Input.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import DrillDown from "$lib/components/settings/DrillDown.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import FormRow from "$lib/components/settings/FormRow.svelte";
@@ -25,6 +27,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const back = `/${repo.ownerHandle}/${repo.name}/settings/access`}
diff --git a/web/src/routes/[handle]/[repo]/settings/hooks/+page.svelte b/web/src/routes/[handle]/[repo]/settings/hooks/+page.svelte
index 03f5d347..7acdcd22 100644
--- a/web/src/routes/[handle]/[repo]/settings/hooks/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/hooks/+page.svelte
@@ -3,6 +3,8 @@
import Avatar from "$lib/components/ui/Avatar.svelte";
import Button from "$lib/components/ui/Button.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import Toggle from "$lib/components/ui/Toggle.svelte";
import SettingsEmpty from "$lib/components/settings/SettingsEmpty.svelte";
import SettingsBlock from "$lib/components/settings/SettingsBlock.svelte";
@@ -52,6 +54,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const base = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/hooks/new/+page.svelte b/web/src/routes/[handle]/[repo]/settings/hooks/new/+page.svelte
index 47db3ce0..2a25fa26 100644
--- a/web/src/routes/[handle]/[repo]/settings/hooks/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/hooks/new/+page.svelte
@@ -5,6 +5,8 @@
import Input from "$lib/components/ui/Input.svelte";
import Checkbox from "$lib/components/ui/Checkbox.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import DrillDown from "$lib/components/settings/DrillDown.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import FormRow from "$lib/components/settings/FormRow.svelte";
@@ -46,6 +48,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const back = `/${repo.ownerHandle}/${repo.name}/settings/hooks`}
diff --git a/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte b/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte
index f0d0c990..863bc8e2 100644
--- a/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte
@@ -6,6 +6,8 @@
import Radio from "$lib/components/ui/Radio.svelte";
import Checkbox from "$lib/components/ui/Checkbox.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import DrillDown from "$lib/components/settings/DrillDown.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import FormRow from "$lib/components/settings/FormRow.svelte";
@@ -42,6 +44,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const back = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte b/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte
index 1ab17bd8..8a11998a 100644
--- a/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte
@@ -4,6 +4,8 @@
import Button from "$lib/components/ui/Button.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
import Select from "$lib/components/ui/Select.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import SettingsBlock from "$lib/components/settings/SettingsBlock.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import SettingsRow from "$lib/components/settings/SettingsRow.svelte";
@@ -42,6 +44,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const base = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/pipelines/secrets/new/+page.svelte b/web/src/routes/[handle]/[repo]/settings/pipelines/secrets/new/+page.svelte
index 2a9010ed..1fb97e93 100644
--- a/web/src/routes/[handle]/[repo]/settings/pipelines/secrets/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/pipelines/secrets/new/+page.svelte
@@ -4,6 +4,8 @@
import Button from "$lib/components/ui/Button.svelte";
import Input from "$lib/components/ui/Input.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import DrillDown from "$lib/components/settings/DrillDown.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import FormRow from "$lib/components/settings/FormRow.svelte";
@@ -26,6 +28,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const back = `/${repo.ownerHandle}/${repo.name}/settings/pipelines`}
diff --git a/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte b/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte
index 27963966..25e8bb21 100644
--- a/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte
@@ -4,6 +4,8 @@
import Button from "$lib/components/ui/Button.svelte";
import Input from "$lib/components/ui/Input.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import DrillDown from "$lib/components/settings/DrillDown.svelte";
import SettingsList from "$lib/components/settings/SettingsList.svelte";
import FormRow from "$lib/components/settings/FormRow.svelte";
@@ -29,6 +31,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const back = `/${repo.ownerHandle}/${repo.name}/settings`}
diff --git a/web/src/routes/[handle]/[repo]/settings/sites/+page.svelte b/web/src/routes/[handle]/[repo]/settings/sites/+page.svelte
index 359bf89b..9fc6bca2 100644
--- a/web/src/routes/[handle]/[repo]/settings/sites/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/settings/sites/+page.svelte
@@ -4,6 +4,8 @@
import Pending from "$lib/components/ui/Pending.svelte";
import Radio from "$lib/components/ui/Radio.svelte";
import Select from "$lib/components/ui/Select.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import settingsSettingspanelContentSkeletonFixture from "$lib/bones/settings-settingspanel--content-skeleton-fixture.bones.json";
import Toggle from "$lib/components/ui/Toggle.svelte";
import Tag from "$lib/components/ui/Tag.svelte";
import SettingsEmpty from "$lib/components/settings/SettingsEmpty.svelte";
@@ -54,6 +56,9 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{@const subPath = `${domain}/${repo.name}`}
diff --git a/web/src/routes/[handle]/[repo]/stars/+page.svelte b/web/src/routes/[handle]/[repo]/stars/+page.svelte
index 75cae6ce..71ee84f4 100644
--- a/web/src/routes/[handle]/[repo]/stars/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/stars/+page.svelte
@@ -1,7 +1,9 @@
@@ -10,6 +12,9 @@
Starred by
+ {#snippet skeleton()}
+
+ {/snippet}
{@const starrers = await data.starrers}
{#if starrers.length === 0}
No stars yet.
diff --git a/web/src/routes/[handle]/[repo]/tags/+page.svelte b/web/src/routes/[handle]/[repo]/tags/+page.svelte
index 65e3bb80..46de9017 100644
--- a/web/src/routes/[handle]/[repo]/tags/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/tags/+page.svelte
@@ -2,6 +2,8 @@
import { goto } from "$app/navigation";
import { resolve } from "$app/paths";
import TagCard from "$lib/components/repo/TagCard.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import tagList from "$lib/bones/repo-taglist--skeleton-fixture.bones.json";
import Pagination from "$lib/components/ui/Pagination.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
import TabPanel from "$lib/components/ui/TabPanel.svelte";
@@ -22,6 +24,9 @@
Tags
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
{#each await data.tags as tag (tag.name)}
diff --git a/web/src/routes/[handle]/[repo]/tags/[tag]/+page.svelte b/web/src/routes/[handle]/[repo]/tags/[tag]/+page.svelte
index 5371ec67..ce32d7eb 100644
--- a/web/src/routes/[handle]/[repo]/tags/[tag]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/tags/[tag]/+page.svelte
@@ -1,5 +1,7 @@
+ {#snippet skeleton()}
+
+ {/snippet}
{@const repo = await data.repo}
diff --git a/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte b/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
index 0d27ce76..c5b7c085 100644
--- a/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
@@ -2,6 +2,9 @@
import FileTree from "$lib/components/repo/FileTree.svelte";
import LastCommitPanel from "$lib/components/repo/LastCommitPanel.svelte";
import Readme from "$lib/components/repo/Readme.svelte";
+ import Bones from "$lib/components/ui/Bones.svelte";
+ import repoReadmeRenderedSanitizedHtml from "$lib/bones/repo-readme--rendered-sanitized-html.bones.json";
+ import repoTreeviewSkeletonFixture from "$lib/bones/repo-treeview--skeleton-fixture.bones.json";
import TreeHeader from "$lib/components/repo/TreeHeader.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
import TabPanel from "$lib/components/ui/TabPanel.svelte";
@@ -11,6 +14,10 @@
+ {#snippet skeleton()}
+
+ {/snippet}
+
{@const repo = await data.repo}
{@const tree = await data.tree}
@@ -42,6 +49,10 @@
{#if tree.readme}
+ {#snippet skeleton()}
+
+ {/snippet}
+