From 8d624903e9ca4fb682f8853d62ae25caf82edf87 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Wed, 8 Apr 2026 15:18:27 -1000 Subject: [PATCH] feat(desktop): add smart chips for GitHub issue and commit URLs Extend smart link rendering to convert GitHub issue URLs (CircleDot icon) and commit URLs (GitCommitHorizontal icon, 7-char short SHA) into inline chips, matching the existing PR chip pattern. Co-Authored-By: Claude Opus 4.6 (1M context) --- desktop/src/shared/ui/markdown.tsx | 69 +++++++++++++++++++-------- desktop/tests/e2e/smart-links.spec.ts | 44 +++++++++++++++++ 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx index 84fcf0b9b..8dd10b3d5 100644 --- a/desktop/src/shared/ui/markdown.tsx +++ b/desktop/src/shared/ui/markdown.tsx @@ -1,4 +1,4 @@ -import { GitPullRequest } from "lucide-react"; +import { CircleDot, GitCommitHorizontal, GitPullRequest } from "lucide-react"; import * as React from "react"; import ReactMarkdown, { type Components } from "react-markdown"; import remarkBreaks from "remark-breaks"; @@ -15,6 +15,12 @@ import remarkMentions from "@/shared/lib/remarkMentions"; const GITHUB_PR_RE = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/; +const GITHUB_ISSUE_RE = + /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/issues\/(\d+)\/?$/; + +const GITHUB_COMMIT_RE = + /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/commit\/([0-9a-f]{7,40})\/?$/; + type MarkdownProps = { channelNames?: string[]; className?: string; @@ -46,25 +52,50 @@ function createMarkdownComponents( return { a: ({ children, href, ...props }) => { - const prMatch = href ? GITHUB_PR_RE.exec(href) : null; - if (prMatch) { - const [, owner, repo, number] = prMatch; - return ( - - - {href} - - ); + if (href) { + let Icon: React.ComponentType<{ className?: string }> | null = null; + let label: string | null = null; + + const prMatch = GITHUB_PR_RE.exec(href); + if (prMatch) { + const [, owner, repo, number] = prMatch; + Icon = GitPullRequest; + label = `${owner}/${repo}#${number}`; + } + + const issueMatch = !Icon ? GITHUB_ISSUE_RE.exec(href) : null; + if (issueMatch) { + const [, owner, repo, number] = issueMatch; + Icon = CircleDot; + label = `${owner}/${repo}#${number}`; + } + + const commitMatch = !Icon ? GITHUB_COMMIT_RE.exec(href) : null; + if (commitMatch) { + const [, owner, repo, sha] = commitMatch; + Icon = GitCommitHorizontal; + label = `${owner}/${repo}@${sha.slice(0, 7)}`; + } + + if (Icon && label) { + return ( + + + {href} + + ); + } } + return ( { + const issueUrl = "https://github.com/block/sprout/issues/99"; + const message = `See ${issueUrl} for context`; + + await page.goto("/"); + await page.getByTestId("channel-general").click(); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + + const input = page.getByTestId("message-input"); + await input.fill(message); + await page.getByTestId("send-message").click(); + + const lastRow = page.getByTestId("message-row").last(); + + const issueChip = lastRow.locator("a", { hasText: "block/sprout#99" }); + await expect(issueChip).toBeVisible(); + await expect(issueChip).toHaveAttribute("href", issueUrl); + await expect(issueChip.locator("svg")).toBeVisible(); +}); + +test("GitHub commit URL renders as an inline smart chip", async ({ page }) => { + const commitUrl = + "https://github.com/block/sprout/commit/abc1234def5678901234567890abcdef12345678"; + const message = `Reverted in ${commitUrl}`; + + await page.goto("/"); + await page.getByTestId("channel-general").click(); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + + const input = page.getByTestId("message-input"); + await input.fill(message); + await page.getByTestId("send-message").click(); + + const lastRow = page.getByTestId("message-row").last(); + + // Should show short SHA + const commitChip = lastRow.locator("a", { + hasText: "block/sprout@abc1234", + }); + await expect(commitChip).toBeVisible(); + await expect(commitChip).toHaveAttribute("href", commitUrl); + await expect(commitChip.locator("svg")).toBeVisible(); +}); + test("non-PR GitHub links render as regular links", async ({ page }) => { const repoUrl = "https://github.com/block/sprout"; const message = `Check out ${repoUrl}`;