diff --git a/web/package.json b/web/package.json index 0c8b992e..edd178d6 100644 --- a/web/package.json +++ b/web/package.json @@ -39,7 +39,7 @@ "globals": "^17.6.0", "prettier": "^3.8.3", "prettier-plugin-svelte": "^4.1.0", - "prettier-plugin-tailwindcss": "^0.8.0", + "prettier-plugin-tailwindcss": "^0.8.1", "svelte": "^5.56.1", "svelte-check": "^4.6.0", "tailwindcss": "^4.3.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 8e0c6a0b..bbf2b2a0 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -106,8 +106,8 @@ importers: specifier: ^4.1.0 version: 4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.63.0)) prettier-plugin-tailwindcss: - specifier: ^0.8.0 - version: 0.8.0(prettier-plugin-svelte@4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.63.0)))(prettier@3.9.4) + specifier: ^0.8.1 + version: 0.8.1(prettier-plugin-svelte@4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.63.0)))(prettier@3.9.4) storybook: specifier: ^10.5.0 version: 10.5.0(@types/react@19.2.17)(prettier@3.9.4)(react@19.2.7) @@ -2283,8 +2283,8 @@ packages: prettier: ^3.0.0 svelte: ^5.0.0 - prettier-plugin-tailwindcss@0.8.0: - resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==} + prettier-plugin-tailwindcss@0.8.1: + resolution: {integrity: sha512-iaFMYqDsE4ffdDkn5qup0j5f2aCEBFZrdrZnvu9QKTlWx/iGPeQ4HHu7b7fCPMxeo9nwQBiOAh2nSypdFYWJkw==} engines: {node: '>=20.19'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -4752,7 +4752,7 @@ snapshots: prettier: 3.9.4 svelte: 5.56.4(@typescript-eslint/types@8.63.0) - prettier-plugin-tailwindcss@0.8.0(prettier-plugin-svelte@4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.63.0)))(prettier@3.9.4): + prettier-plugin-tailwindcss@0.8.1(prettier-plugin-svelte@4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.63.0)))(prettier@3.9.4): dependencies: prettier: 3.9.4 optionalDependencies: diff --git a/web/prettier.config.js b/web/prettier.config.js index 6ec7ab57..104c4c36 100644 --- a/web/prettier.config.js +++ b/web/prettier.config.js @@ -6,7 +6,8 @@ const config = { printWidth: 100, plugins: ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], overrides: [{ files: "*.svelte", options: { parser: "svelte" } }], - tailwindStylesheet: "./src/app.css" + tailwindStylesheet: "./src/app.css", + tailwindFunctions: ["tv"] }; export default config; diff --git a/web/src/lib/components/home/MarketingHome.svelte b/web/src/lib/components/home/MarketingHome.svelte index 6d451101..3e4fb15b 100644 --- a/web/src/lib/components/home/MarketingHome.svelte +++ b/web/src/lib/components/home/MarketingHome.svelte @@ -45,10 +45,10 @@ {/snippet} -
+

tightly-knit
social coding.

diff --git a/web/src/lib/components/profile/FollowCard.svelte b/web/src/lib/components/profile/FollowCard.svelte index 74ce3d51..19486bb3 100644 --- a/web/src/lib/components/profile/FollowCard.svelte +++ b/web/src/lib/components/profile/FollowCard.svelte @@ -25,15 +25,15 @@
{person.handle} {#if person.description} -

{person.description}

+

{person.description}

{/if}
- import { resolve } from "$app/paths"; import SquareChartGantt from "$icon/square-chart-gantt"; import BookMarked from "$icon/book-marked"; import Star from "$icon/star"; import LineSquiggle from "$icon/line-squiggle"; import Shield from "$icon/shield"; - import type { Component } from "svelte"; - import type { SvelteHTMLElements } from "svelte/elements"; + import Tabs, { type TabDef } from "$lib/components/ui/Tabs.svelte"; import type { ProfileCounts } from "./types"; interface Props { @@ -17,51 +15,27 @@ let { handle, active, counts }: Props = $props(); - interface TabDef { - id: string; - label: string; - icon: Component; - count?: number; - } + const hrefFor = (id: string) => (id === "overview" ? `/${handle}` : `/${handle}?tab=${id}`); const tabs = $derived([ - { id: "overview", label: "Overview", icon: SquareChartGantt }, - { id: "repos", label: "Repositories", icon: BookMarked, count: counts.repos }, - { id: "starred", label: "Starred", icon: Star, count: counts.stars }, - { id: "strings", label: "Strings", icon: LineSquiggle, count: counts.strings }, - { id: "vouches", label: "Vouches", icon: Shield } + { id: "overview", label: "Overview", icon: SquareChartGantt, href: hrefFor("overview") }, + { + id: "repos", + label: "Repositories", + icon: BookMarked, + count: counts.repos, + href: hrefFor("repos") + }, + { id: "starred", label: "Starred", icon: Star, count: counts.stars, href: hrefFor("starred") }, + { + id: "strings", + label: "Strings", + icon: LineSquiggle, + count: counts.strings, + href: hrefFor("strings") + }, + { id: "vouches", label: "Vouches", icon: Shield, href: hrefFor("vouches") } ]); - - const hrefFor = (id: string) => - resolve((id === "overview" ? `/${handle}` : `/${handle}?tab=${id}`) as "/"); - + diff --git a/web/src/lib/components/profile/tabs/OverviewTab.svelte b/web/src/lib/components/profile/tabs/OverviewTab.svelte index 16ef03e7..b05d2bda 100644 --- a/web/src/lib/components/profile/tabs/OverviewTab.svelte +++ b/web/src/lib/components/profile/tabs/OverviewTab.svelte @@ -1,6 +1,6 @@
-
-

Pinned repositories

- {#if pinned.length === 0} - - {:else} -
- {#each pinned as repo (repo.rkey)} - - {/each} -
- {/if} -
+
+
+ {#each pinned as repo (repo.rkey)} + + {/each} +
+

Activity

diff --git a/web/src/lib/components/profile/tabs/PeopleTab.svelte b/web/src/lib/components/profile/tabs/PeopleTab.svelte index e7c6726f..6dabe276 100644 --- a/web/src/lib/components/profile/tabs/PeopleTab.svelte +++ b/web/src/lib/components/profile/tabs/PeopleTab.svelte @@ -1,6 +1,6 @@ -
-

{title}

- {#if people.length === 0} - - {:else} -
- {#each people as person (person.did)} - - {/each} -
- {/if} -
+
+ {#each people as person (person.did)} + + {/each} +
diff --git a/web/src/lib/components/profile/tabs/RepoListTab.svelte b/web/src/lib/components/profile/tabs/RepoListTab.svelte index 26d8abfb..0e68a395 100644 --- a/web/src/lib/components/profile/tabs/RepoListTab.svelte +++ b/web/src/lib/components/profile/tabs/RepoListTab.svelte @@ -2,7 +2,7 @@ import { page } from "$app/state"; import { resolve } from "$app/paths"; import RepoCard from "$lib/components/repo/RepoCard.svelte"; - import EmptyState from "../EmptyState.svelte"; + import Section from "$lib/components/ui/Section.svelte"; import type { RepoCardData } from "../types"; import Search from "$icon/search"; import X from "$icon/x"; @@ -11,43 +11,39 @@ let searchQuery = $derived(page.url.searchParams.get("q") ?? ""); -
-
-
- -
- - {#if repos.length === 0} - - {:else} -
- {#each repos as repo (repo.rkey)} - - {/each} + + +
- {/if} -
+ {/snippet} + {#each repos as repo (repo.rkey)} + + {/each} +
diff --git a/web/src/lib/components/profile/tabs/StarredTab.svelte b/web/src/lib/components/profile/tabs/StarredTab.svelte index 0c3ec560..ab45d81a 100644 --- a/web/src/lib/components/profile/tabs/StarredTab.svelte +++ b/web/src/lib/components/profile/tabs/StarredTab.svelte @@ -1,34 +1,27 @@ -
-

Starred

- {#if stars.length === 0} - - {:else} -
- {#each stars as star (star.uri)} - {#if star.kind === "repo"} - - {:else} - - {/if} - {/each} -
- {/if} -
+
+ {#each stars as star (star.uri)} + {#if star.kind === "repo"} + + {:else} + + {/if} + {/each} +
diff --git a/web/src/lib/components/profile/tabs/StringListTab.svelte b/web/src/lib/components/profile/tabs/StringListTab.svelte index 94b448de..80e79c9e 100644 --- a/web/src/lib/components/profile/tabs/StringListTab.svelte +++ b/web/src/lib/components/profile/tabs/StringListTab.svelte @@ -1,20 +1,13 @@ -
-

Strings

- {#if strings.length === 0} - - {:else} -
- {#each strings as entry (entry.rkey)} - - {/each} -
- {/if} -
+
+ {#each strings as entry (entry.rkey)} + + {/each} +
diff --git a/web/src/lib/components/profile/tabs/VouchTab.svelte b/web/src/lib/components/profile/tabs/VouchTab.svelte index 45a23b33..9a996850 100644 --- a/web/src/lib/components/profile/tabs/VouchTab.svelte +++ b/web/src/lib/components/profile/tabs/VouchTab.svelte @@ -1,6 +1,6 @@ -
-

Vouches

- {#if vouches.length === 0} - - {:else} -
-
- {#each vouches as vouch (vouch.uri)} - - {/each} -
- {/if} -
+
+
+ {#each vouches as vouch (vouch.uri)} + + {/each} +
diff --git a/web/src/lib/components/repo/RepoCard.svelte b/web/src/lib/components/repo/RepoCard.svelte index c7d6a8e8..74a5f184 100644 --- a/web/src/lib/components/repo/RepoCard.svelte +++ b/web/src/lib/components/repo/RepoCard.svelte @@ -27,7 +27,7 @@ class="flex min-h-32 flex-col gap-1 rounded border border-border-default bg-background-default px-6 py-4 shadow-sm" >
-
- + {#snippet trigger()}
{:else if user} - + {#snippet trigger()} {#if user.avatar} { - src; - failed = false; + if (src) failed = false; }); diff --git a/web/src/lib/components/ui/Button.svelte b/web/src/lib/components/ui/Button.svelte index 61714f1f..f87ad0ae 100644 --- a/web/src/lib/components/ui/Button.svelte +++ b/web/src/lib/components/ui/Button.svelte @@ -3,7 +3,7 @@ export const button = tv({ base: [ - "inline-flex cursor-pointer select-none items-center justify-center overflow-hidden rounded-l-(--btn-radius-l) rounded-r-(--btn-radius-r) [--btn-radius:var(--radius-sm)] [--btn-radius-l:var(--btn-radius)] [--btn-radius-r:var(--btn-radius)] no-underline", + "inline-flex cursor-pointer items-center justify-center overflow-hidden rounded-l-(--btn-radius-l) rounded-r-(--btn-radius-r) no-underline select-none [--btn-radius-l:var(--btn-radius)] [--btn-radius-r:var(--btn-radius)] [--btn-radius:var(--radius-sm)]", "transition-colors duration-150 ease-in-out", "hover:no-underline", "focus-visible:outline-2 focus-visible:outline-offset-2", @@ -22,7 +22,7 @@ "focus-visible:outline-background-success-emphasis" ], ghost: [ - "bg-transparent text-foreground-muted border border-transparent", + "border border-transparent bg-transparent text-foreground-muted", "hover:bg-background-inset", "focus-visible:outline-border-focus" ], @@ -60,7 +60,7 @@ variant: "default", insetShadow: true, class: [ - "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-l-[max(calc(var(--btn-radius-l)-2px),0px)] before:rounded-r-[max(calc(var(--btn-radius-r)-2px),0px)] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150", + "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-l-[max(calc(var(--btn-radius-l)-2px),0px)] before:rounded-r-[max(calc(var(--btn-radius-r)-2px),0px)] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150 before:content-['']", "hover:before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-strong-hover)]", "active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-shadow-inner-subtle)]", "disabled:active:translate-y-0" @@ -70,7 +70,7 @@ variant: "primary", insetShadow: true, class: [ - "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-l-[max(calc(var(--btn-radius-l)-2px),0px)] before:rounded-r-[max(calc(var(--btn-radius-r)-2px),0px)] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-alpha-dark-25)] before:transition-all before:duration-150", + "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-l-[max(calc(var(--btn-radius-l)-2px),0px)] before:rounded-r-[max(calc(var(--btn-radius-r)-2px),0px)] before:shadow-[inset_0_-2px_0_0_var(--color-alpha-dark-25)] before:transition-all before:duration-150 before:content-['']", "active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-alpha-dark-50)]", "disabled:active:translate-y-0" ] diff --git a/web/src/lib/components/ui/ButtonCard.svelte b/web/src/lib/components/ui/ButtonCard.svelte index 7665956b..461ca614 100644 --- a/web/src/lib/components/ui/ButtonCard.svelte +++ b/web/src/lib/components/ui/ButtonCard.svelte @@ -3,7 +3,7 @@ export const buttonCard = tv({ base: [ - "flex h-40 w-48 cursor-pointer gap-3 rounded-(--btn-radius) [--btn-radius:var(--radius-sm)] px-4 py-3 select-none no-underline", + "flex h-40 w-48 cursor-pointer gap-3 rounded-(--btn-radius) px-4 py-3 no-underline select-none [--btn-radius:var(--radius-sm)]", "transition-colors duration-150 ease-in-out", "hover:no-underline", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus", @@ -15,10 +15,7 @@ "border border-border-default bg-background-default", "hover:bg-background-subtle" ], - ghost: [ - "border border-transparent bg-transparent", - "hover:bg-background-inset" - ] + ghost: ["border border-transparent bg-transparent", "hover:bg-background-inset"] }, insetShadow: { true: "", @@ -31,7 +28,7 @@ variant: "default", insetShadow: true, class: [ - "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[max(calc(var(--btn-radius)-2px),0px)] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150", + "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[max(calc(var(--btn-radius)-2px),0px)] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150 before:content-['']", "hover:before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-strong-hover)]", "active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-shadow-inner-subtle)]", "disabled:active:translate-y-0" @@ -90,7 +87,7 @@ {#snippet content()}
{#if title} -
+
{#if Icon}
+
diff --git a/web/src/lib/components/ui/Logo.svelte b/web/src/lib/components/ui/Logo.svelte index cf7bf5ef..65ba3611 100644 --- a/web/src/lib/components/ui/Logo.svelte +++ b/web/src/lib/components/ui/Logo.svelte @@ -73,7 +73,7 @@ {@render mark()} alpha diff --git a/web/src/lib/components/ui/Pagination.svelte b/web/src/lib/components/ui/Pagination.svelte index c9a7fb57..ef3d5d4b 100644 --- a/web/src/lib/components/ui/Pagination.svelte +++ b/web/src/lib/components/ui/Pagination.svelte @@ -97,7 +97,7 @@ {:else} diff --git a/web/src/lib/components/ui/Section.stories.svelte b/web/src/lib/components/ui/Section.stories.svelte new file mode 100644 index 00000000..1a573e6c --- /dev/null +++ b/web/src/lib/components/ui/Section.stories.svelte @@ -0,0 +1,50 @@ + + +{#snippet items()} +
First item
+
Second item
+
Third item
+{/snippet} + + +
+ {@render items()} +
+
+ + + +
+ {@render items()} +
+
+ + +
+ {@render items()} +
+
+ + + +
+ {#snippet header()} +

Up to four pinned repositories.

+ {/snippet} + {@render items()} +
+
diff --git a/web/src/lib/components/ui/Section.svelte b/web/src/lib/components/ui/Section.svelte new file mode 100644 index 00000000..712ed5e5 --- /dev/null +++ b/web/src/lib/components/ui/Section.svelte @@ -0,0 +1,36 @@ + + +
+ {#if title} +

{title}

+ {/if} + {@render header?.()} + {#if empty} + + {:else} +
+ {@render children()} +
+ {/if} +
diff --git a/web/src/lib/components/ui/Select.svelte b/web/src/lib/components/ui/Select.svelte index cf206b4d..f5bfa2f8 100644 --- a/web/src/lib/components/ui/Select.svelte +++ b/web/src/lib/components/ui/Select.svelte @@ -2,7 +2,7 @@ import { tv, type VariantProps } from "tailwind-variants"; export const select = tv({ - base: "w-full appearance-none rounded border bg-background-default px-3 py-2 pr-9 text-sm outline-none transition-colors disabled:cursor-not-allowed", + base: "w-full appearance-none rounded border bg-background-default px-3 py-2 pr-9 text-sm transition-colors outline-none disabled:cursor-not-allowed", variants: { error: { false: @@ -31,7 +31,7 @@ export type SelectVariants = VariantProps; const chevron = tv({ - base: "pointer-events-none absolute right-3 top-1/2 size-3.5 -translate-y-1/2 text-foreground-subtle", + base: "pointer-events-none absolute top-1/2 right-3 size-3.5 -translate-y-1/2 text-foreground-subtle", variants: { error: { true: "text-foreground-danger" diff --git a/web/src/lib/components/ui/Tabs.stories.svelte b/web/src/lib/components/ui/Tabs.stories.svelte new file mode 100644 index 00000000..8f4e83aa --- /dev/null +++ b/web/src/lib/components/ui/Tabs.stories.svelte @@ -0,0 +1,32 @@ + + + + + diff --git a/web/src/lib/components/ui/Tabs.svelte b/web/src/lib/components/ui/Tabs.svelte new file mode 100644 index 00000000..e12b7b39 --- /dev/null +++ b/web/src/lib/components/ui/Tabs.svelte @@ -0,0 +1,56 @@ + + + + +
diff --git a/web/src/lib/components/ui/Tag.svelte b/web/src/lib/components/ui/Tag.svelte index 09b86af8..ab69fbef 100644 --- a/web/src/lib/components/ui/Tag.svelte +++ b/web/src/lib/components/ui/Tag.svelte @@ -2,7 +2,7 @@ import { tv, type VariantProps } from "tailwind-variants"; export const tag = tv({ - base: "inline-flex select-none items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium leading-none", + base: "inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs leading-none font-medium select-none", variants: { color: { default: "border border-border-default bg-background-default text-foreground-default", diff --git a/web/src/lib/components/ui/Textarea.svelte b/web/src/lib/components/ui/Textarea.svelte index da0511e4..61f83d3b 100644 --- a/web/src/lib/components/ui/Textarea.svelte +++ b/web/src/lib/components/ui/Textarea.svelte @@ -80,7 +80,7 @@
{#if iconLeft} {@const IconLeft = iconLeft} -
diff --git a/web/src/routes/[handle]/+layout.svelte b/web/src/routes/[handle]/+layout.svelte index 701871bf..87beb4a8 100644 --- a/web/src/routes/[handle]/+layout.svelte +++ b/web/src/routes/[handle]/+layout.svelte @@ -2,7 +2,7 @@ import { page } from "$app/state"; import { setContext } from "svelte"; import { createProfileCounts, PROFILE_COUNTS_KEY } from "$lib/components/profile/counts.svelte"; - import EmptyState from "$lib/components/profile/EmptyState.svelte"; + import EmptyState from "$lib/components/ui/EmptyState.svelte"; import ProfileCard from "$lib/components/profile/ProfileCard.svelte"; import ProfileTabs from "$lib/components/profile/ProfileTabs.svelte"; diff --git a/web/src/routes/login/+page.svelte b/web/src/routes/login/+page.svelte index 145192ed..2449209a 100644 --- a/web/src/routes/login/+page.svelte +++ b/web/src/routes/login/+page.svelte @@ -43,7 +43,7 @@ tangled -

tightly-knit social coding.

+

tightly-knit social coding.

@@ -60,7 +60,7 @@ inputmode="url" required placeholder="akshay.tngl.sh" - class="w-full rounded border border-border-default bg-background-default p-3 text-lg text-foreground-default placeholder:text-foreground-placeholder focus:outline-none focus:ring-1 focus:ring-border-strong md:text-base" + class="w-full rounded border border-border-default bg-background-default p-3 text-lg text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none md:text-base" />

Use your AT Protocol handle to log in. If diff --git a/web/src/routes/repo/new/+page.svelte b/web/src/routes/repo/new/+page.svelte index 66427067..c16b7ac6 100644 --- a/web/src/routes/repo/new/+page.svelte +++ b/web/src/routes/repo/new/+page.svelte @@ -1,4 +1,5 @@