From 96042bee432f9b149b90d7d0cce018b26c71cd65 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Tue, 25 Jun 2024 13:06:15 +0200 Subject: [PATCH] Add InlineTitle component This new component doesn't rely on marked.js but escapes any special characters and then replaces any backtick with a HTML tag to render any inline codeblocks in all titles. --- src/components/InlineMarkdown.svelte | 5 +--- src/components/Markdown.svelte | 4 +-- src/lib/markdown.ts | 17 +---------- src/lib/utils.ts | 4 +++ src/views/projects/Cob/CobCommitTeaser.svelte | 4 +-- src/views/projects/Commit.svelte | 7 ++--- src/views/projects/Commit/CommitTeaser.svelte | 4 +-- src/views/projects/Issue.svelte | 7 ++--- src/views/projects/Issue/IssueTeaser.svelte | 4 +-- src/views/projects/Patch.svelte | 7 ++--- src/views/projects/Patch/PatchTeaser.svelte | 4 +-- .../projects/components/InlineTitle.svelte | 28 +++++++++++++++++++ tests/e2e/project/issue.spec.ts | 2 +- tests/unit/utils.test.ts | 17 +++++++++++ 14 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 src/views/projects/components/InlineTitle.svelte diff --git a/src/components/InlineMarkdown.svelte b/src/components/InlineMarkdown.svelte index 5ea4b5b0b8..47fe3ce72b 100644 --- a/src/components/InlineMarkdown.svelte +++ b/src/components/InlineMarkdown.svelte @@ -7,16 +7,13 @@ import { Renderer } from "@app/lib/markdown"; export let content: string; - export let stripEmphasizedStyling: boolean = false; export let fontSize: "tiny" | "small" | "regular" | "medium" | "large" = "small"; const render = (content: string): string => dompurify.sanitize( markdown.parseInline(content, { - renderer: new Renderer($activeUnloadedRouteStore, { - stripEmphasizedStyling, - }), + renderer: new Renderer($activeUnloadedRouteStore), }) as string, ); diff --git a/src/components/Markdown.svelte b/src/components/Markdown.svelte index c5d7612ff9..e793e03de8 100644 --- a/src/components/Markdown.svelte +++ b/src/components/Markdown.svelte @@ -93,9 +93,7 @@ function render(content: string): string { return dompurify.sanitize( markdownWithExtensions.parse(content, { - renderer: new Renderer($activeUnloadedRouteStore, { - stripEmphasizedStyling: false, - }), + renderer: new Renderer($activeUnloadedRouteStore), breaks, }) as string, ); diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index 7dd99a9588..ac0227009d 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -40,19 +40,14 @@ const anchorMarkedExtension = { export class Renderer extends BaseRenderer { #route: Route; - #stripEmphasizedStyling: boolean | undefined; /** * If `baseUrl` is provided, all hrefs attributes in anchor tags, except those * starting with `#`, are resolved with respect to `baseUrl` */ - constructor( - activeUnloadedRoute: Route, - { stripEmphasizedStyling }: { stripEmphasizedStyling: boolean }, - ) { + constructor(activeUnloadedRoute: Route) { super(); this.#route = activeUnloadedRoute; - this.#stripEmphasizedStyling = stripEmphasizedStyling; } // Overwrites the rendering of heading tokens. // Since there are possible non ASCII characters in headings, @@ -69,16 +64,6 @@ export class Renderer extends BaseRenderer { return `${text}`; } - strong({ tokens }: Tokens.Strong) { - const text = this.parser.parseInline(tokens); - return this.#stripEmphasizedStyling ? text : `${text}`; - } - - em({ tokens }: Tokens.Em) { - const text = this.parser.parseInline(tokens); - return this.#stripEmphasizedStyling ? text : `${text}`; - } - link({ href, title, tokens }: Tokens.Link): string { const text = this.parser.parseInline(tokens); if (href.startsWith("#")) { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4555877a48..d7bbf86ae2 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -13,6 +13,10 @@ export function formatLocationHash(hash: string | null): number | null { return null; } +export function formatInlineTitle(input: string): string { + return input.replaceAll(/`([^`]+)`/g, "$1"); +} + export function parseNodeId( nid: string, ): { prefix: string; pubkey: string } | undefined { diff --git a/src/views/projects/Cob/CobCommitTeaser.svelte b/src/views/projects/Cob/CobCommitTeaser.svelte index a016160668..93ca7d1fce 100644 --- a/src/views/projects/Cob/CobCommitTeaser.svelte +++ b/src/views/projects/Cob/CobCommitTeaser.svelte @@ -8,7 +8,7 @@ import IconButton from "@app/components/IconButton.svelte"; import IconSmall from "@app/components/IconSmall.svelte"; import Id from "@app/components/Id.svelte"; - import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; + import InlineTitle from "@app/views/projects/components/InlineTitle.svelte"; import Link from "@app/components/Link.svelte"; export let baseUrl: BaseUrl; @@ -68,7 +68,7 @@ commit: commit.id, }}>
- +
{#if commit.description} diff --git a/src/views/projects/Commit.svelte b/src/views/projects/Commit.svelte index a53015749b..263b0db67e 100644 --- a/src/views/projects/Commit.svelte +++ b/src/views/projects/Commit.svelte @@ -5,7 +5,7 @@ import Changeset from "@app/views/projects/Changeset.svelte"; import CommitAuthorship from "@app/views/projects/Commit/CommitAuthorship.svelte"; import IconSmall from "@app/components/IconSmall.svelte"; - import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; + import InlineTitle from "@app/views/projects/components/InlineTitle.svelte"; import Layout from "./Layout.svelte"; import Link from "@app/components/Link.svelte"; import Share from "./Share.svelte"; @@ -50,10 +50,7 @@
- +
- +
diff --git a/src/views/projects/Issue.svelte b/src/views/projects/Issue.svelte index 7c2a829ab3..0850589efa 100644 --- a/src/views/projects/Issue.svelte +++ b/src/views/projects/Issue.svelte @@ -37,7 +37,7 @@ import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte"; import IconSmall from "@app/components/IconSmall.svelte"; import Id from "@app/components/Id.svelte"; - import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; + import InlineTitle from "@app/views/projects/components/InlineTitle.svelte"; import LabelInput from "./Cob/LabelInput.svelte"; import Layout from "./Layout.svelte"; import Markdown from "@app/components/Markdown.svelte"; @@ -499,10 +499,7 @@ No title {:else}
- +
{/if}
diff --git a/src/views/projects/Issue/IssueTeaser.svelte b/src/views/projects/Issue/IssueTeaser.svelte index 11e46a6991..7fa73ada02 100644 --- a/src/views/projects/Issue/IssueTeaser.svelte +++ b/src/views/projects/Issue/IssueTeaser.svelte @@ -4,7 +4,7 @@ import { absoluteTimestamp, formatTimestamp } from "@app/lib/utils"; import IconSmall from "@app/components/IconSmall.svelte"; - import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; + import InlineTitle from "@app/views/projects/components/InlineTitle.svelte"; import Link from "@app/components/Link.svelte"; import NodeId from "@app/components/NodeId.svelte"; @@ -93,7 +93,7 @@ {#if !issue.title} No title {:else} - + {/if}
diff --git a/src/views/projects/Patch.svelte b/src/views/projects/Patch.svelte index e911f71066..bde15fce92 100644 --- a/src/views/projects/Patch.svelte +++ b/src/views/projects/Patch.svelte @@ -75,7 +75,7 @@ import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte"; import IconSmall from "@app/components/IconSmall.svelte"; import Id from "@app/components/Id.svelte"; - import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; + import InlineTitle from "@app/views/projects/components/InlineTitle.svelte"; import LabelInput from "@app/views/projects/Cob/LabelInput.svelte"; import Layout from "@app/views/projects/Layout.svelte"; import Link from "@app/components/Link.svelte"; @@ -724,10 +724,7 @@ No title {:else}
- +
{/if}
diff --git a/src/views/projects/Patch/PatchTeaser.svelte b/src/views/projects/Patch/PatchTeaser.svelte index 724b8ceb16..bd711ed1ea 100644 --- a/src/views/projects/Patch/PatchTeaser.svelte +++ b/src/views/projects/Patch/PatchTeaser.svelte @@ -5,7 +5,7 @@ import { absoluteTimestamp, formatTimestamp } from "@app/lib/utils"; import IconSmall from "@app/components/IconSmall.svelte"; - import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; + import InlineTitle from "@app/views/projects/components/InlineTitle.svelte"; import Link from "@app/components/Link.svelte"; import NodeId from "@app/components/NodeId.svelte"; @@ -105,7 +105,7 @@ node: baseUrl, patch: patch.id, }}> - + {#if patch.labels.length > 0} + import dompurify from "dompurify"; + import escape from "lodash/escape"; + import { formatInlineTitle } from "@app/lib/utils"; + + export let content: string; + export let fontSize: "tiny" | "small" | "regular" | "medium" | "large" = + "small"; + + + + + + {@html dompurify.sanitize(formatInlineTitle(escape(content)))} + diff --git a/tests/e2e/project/issue.spec.ts b/tests/e2e/project/issue.spec.ts index e69501d635..b082222754 100644 --- a/tests/e2e/project/issue.spec.ts +++ b/tests/e2e/project/issue.spec.ts @@ -7,7 +7,7 @@ import { test("navigate single issue", async ({ page }) => { await page.goto(`${cobUrl}/issues`); - await page.getByText("This title has markdown").click(); + await page.getByText("This title has **markdown**").click(); await expect(page).toHaveURL(/\/issues\/[0-9a-f]{40}/); }); diff --git a/tests/unit/utils.test.ts b/tests/unit/utils.test.ts index 5365c7aaf0..ce9b9423d3 100644 --- a/tests/unit/utils.test.ts +++ b/tests/unit/utils.test.ts @@ -18,6 +18,23 @@ describe("Format functions", () => { expect(utils.formatRepositoryId(id)).toEqual(expected); }); + test.each([ + { + input: " Hello `new` world", + expected: " Hello new world", + }, + { input: "Hello `new` world", expected: "Hello new world" }, + { + input: "Hello `new` world `radicle`", + expected: "Hello new world radicle", + }, + { input: "Hello `` world", expected: "Hello `` world" }, + { input: "Hello `", expected: "Hello `" }, + { input: "Hello", expected: "Hello" }, + ])("formatInlineTitle $input => $expected", ({ input, expected }) => { + expect(utils.formatInlineTitle(input)).toEqual(expected); + }); + test.each([ { id: "did:key:z6MkmzRwg47UWQxczLLLFfkEwpBGitjzJ1vKPE8U9ymd6fz6",