diff --git a/web/src/lib/components/comment/Comment.svelte b/web/src/lib/components/comment/Comment.svelte
index 523b542ae..94a46e5e5 100644
--- a/web/src/lib/components/comment/Comment.svelte
+++ b/web/src/lib/components/comment/Comment.svelte
@@ -51,12 +51,12 @@
class={`grid grid-cols-[auto_minmax(0,1fr)] gap-x-1.5 ${variant === "top" ? "border-b border-border-default bg-background-default p-md pb-lg" : "pt-sm pr-md pb-md"}`}
>
-
+
diff --git a/web/src/lib/components/profile/AvatarList.svelte b/web/src/lib/components/profile/AvatarList.svelte
index c3231bf6d..e3a526ae6 100644
--- a/web/src/lib/components/profile/AvatarList.svelte
+++ b/web/src/lib/components/profile/AvatarList.svelte
@@ -39,7 +39,7 @@
{#if popover}
-
+
{@render avatarLink(person)}
{:else}
diff --git a/web/src/lib/components/profile/ProfileBio.svelte b/web/src/lib/components/profile/ProfileBio.svelte
new file mode 100644
index 000000000..deb9b1d7f
--- /dev/null
+++ b/web/src/lib/components/profile/ProfileBio.svelte
@@ -0,0 +1,28 @@
+
+
+
+ {#each segments as segment, i (i)}
+ {#if segment.kind === "mention"}
+
+ @{segment.handle}
+
+ {:else}
+ {segment.text}
+ {/if}
+ {/each}
+
diff --git a/web/src/lib/components/profile/ProfilePopover.stories.svelte b/web/src/lib/components/profile/ProfilePopover.stories.svelte
index c07510b7c..bb9bf6ff8 100644
--- a/web/src/lib/components/profile/ProfilePopover.stories.svelte
+++ b/web/src/lib/components/profile/ProfilePopover.stories.svelte
@@ -51,7 +51,7 @@
component: ProfilePopover,
tags: ["autodocs"],
args: {
- did: DID
+ identifier: DID
}
});
@@ -60,8 +60,11 @@
{#snippet template(args)}
{/snippet}
@@ -71,7 +74,10 @@
{#snippet template(args)}
@@ -82,7 +88,7 @@
{#snippet template()}
@@ -93,8 +99,8 @@
{#snippet template(args)}
{/snippet}
@@ -104,8 +110,8 @@
{#snippet template(args)}
{/snippet}
@@ -115,7 +121,10 @@
{#snippet template()}
diff --git a/web/src/lib/components/profile/ProfilePopover.svelte b/web/src/lib/components/profile/ProfilePopover.svelte
index 2e80b3b2b..75c65efcc 100644
--- a/web/src/lib/components/profile/ProfilePopover.svelte
+++ b/web/src/lib/components/profile/ProfilePopover.svelte
@@ -16,7 +16,7 @@
import type { DirectVouch, ProfileCardData, NetworkVouch } from "$lib/components/profile/types";
interface Props {
- did: string;
+ identifier: string;
disabled?: boolean;
openDelay?: number;
class?: string;
@@ -25,7 +25,7 @@
}
let {
- did,
+ identifier,
disabled = false,
openDelay = 200,
class: className,
@@ -67,7 +67,7 @@
try {
const ctx = createBobbinClient({ serviceUrl: bobbinUrl });
- card = await fetchProfileCard(ctx, { identifier: did, viewerDid });
+ card = await fetchProfileCard(ctx, { identifier, viewerDid });
status = "loaded";
} catch {
status = "error";
diff --git a/web/src/lib/components/profile/UserProfileChrome.svelte b/web/src/lib/components/profile/UserProfileChrome.svelte
index e9325da09..738c60d28 100644
--- a/web/src/lib/components/profile/UserProfileChrome.svelte
+++ b/web/src/lib/components/profile/UserProfileChrome.svelte
@@ -6,6 +6,7 @@
import FollowButton from "$lib/components/profile/FollowButton.svelte";
import NetworkVouches from "$lib/components/profile/NetworkVouches.svelte";
import ProfileEditForm from "$lib/components/profile/ProfileEditForm.svelte";
+ import ProfileBio from "$lib/components/profile/ProfileBio.svelte";
import ProfileMoreMenu from "$lib/components/profile/ProfileMoreMenu.svelte";
import ProfileSocialLinks from "$lib/components/profile/ProfileSocialLinks.svelte";
import ProfileTabs from "$lib/components/profile/ProfileTabs.svelte";
@@ -117,9 +118,10 @@
{:else}
{#if shown?.description}
-
- {shown.description}
-
+
{/if}
{#if shown}
{
+ it("splits mentions out of surrounding text", () => {
+ expect(bioSegments("working on @tangled.org with @Alice.bsky.social.")).toEqual([
+ { kind: "text", text: "working on " },
+ { kind: "mention", handle: "tangled.org" },
+ { kind: "text", text: " with " },
+ { kind: "mention", handle: "alice.bsky.social" },
+ { kind: "text", text: "." }
+ ]);
+ });
+
+ it("leaves emails and dotless names as text", () => {
+ expect(bioSegments("mail me@example.com or @nobody")).toEqual([
+ { kind: "text", text: "mail me@example.com or @nobody" }
+ ]);
+ });
+
+ it("keeps a mention at the very start", () => {
+ expect(bioSegments("@eti.tngl.sh")).toEqual([{ kind: "mention", handle: "eti.tngl.sh" }]);
+ });
+});
diff --git a/web/src/lib/components/profile/bio.ts b/web/src/lib/components/profile/bio.ts
new file mode 100644
index 000000000..c970886fd
--- /dev/null
+++ b/web/src/lib/components/profile/bio.ts
@@ -0,0 +1,17 @@
+export type BioSegment = { kind: "text"; text: string } | { kind: "mention"; handle: string };
+
+const MENTION =
+ /(? {
+ const segments: BioSegment[] = [];
+ let last = 0;
+ for (const match of text.matchAll(MENTION)) {
+ if (match.index > last)
+ segments.push({ kind: "text", text: text.slice(last, match.index) });
+ segments.push({ kind: "mention", handle: match[1].toLowerCase() });
+ last = match.index + match[0].length;
+ }
+ if (last < text.length) segments.push({ kind: "text", text: text.slice(last) });
+ return segments;
+};
diff --git a/web/src/lib/components/profile/organization/OrganizationChrome.svelte b/web/src/lib/components/profile/organization/OrganizationChrome.svelte
index f2be45376..b26608f69 100644
--- a/web/src/lib/components/profile/organization/OrganizationChrome.svelte
+++ b/web/src/lib/components/profile/organization/OrganizationChrome.svelte
@@ -8,6 +8,7 @@
import type { ProfileRecord } from "$lib/api/records";
import { getAuth } from "$lib/auth.svelte";
import FollowButton from "$lib/components/profile/FollowButton.svelte";
+ import ProfileBio from "$lib/components/profile/ProfileBio.svelte";
import ProfileMoreMenu from "$lib/components/profile/ProfileMoreMenu.svelte";
import ProfileSocialLinks from "$lib/components/profile/ProfileSocialLinks.svelte";
import ProfileTabs from "$lib/components/profile/ProfileTabs.svelte";
@@ -104,7 +105,10 @@
{#if profile.description}
- {profile.description}
+
{/if}
diff --git a/web/src/lib/components/repo/pulls/PullCommitRow.svelte b/web/src/lib/components/repo/pulls/PullCommitRow.svelte
index 875e208b7..bea1a86ec 100644
--- a/web/src/lib/components/repo/pulls/PullCommitRow.svelte
+++ b/web/src/lib/components/repo/pulls/PullCommitRow.svelte
@@ -102,7 +102,7 @@
>
{#if showAvatar}
{#if commit.authorHandle && commit.authorDid}
-
+
-
+
+
{:else}
diff --git a/web/src/lib/components/timeline/TimelineActorHeader.svelte b/web/src/lib/components/timeline/TimelineActorHeader.svelte
index 903b59128..230d5ce3f 100644
--- a/web/src/lib/components/timeline/TimelineActorHeader.svelte
+++ b/web/src/lib/components/timeline/TimelineActorHeader.svelte
@@ -19,7 +19,7 @@
} = $props();
-
+
{verb}