From 0daf2f31e698dc0f42a5a0d1dd8fbaae0e6a7653 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Mon, 21 Apr 2025 22:20:30 -0400 Subject: Force the text colour for links in the channel list to something with decent contrast. We were ending up with colours picked by a more specific rule (`a, a:hover, a:visited, a:active`, from `app.css`), which was suppressing the colours we wanted. This is not a particularly elegant solution, but it is _an_ solution. Code organized this way to avoid giving `a` elements a background colour. --- ui/styles/sidebar.css | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'ui') diff --git a/ui/styles/sidebar.css b/ui/styles/sidebar.css index c6aab6a..9a499f2 100644 --- a/ui/styles/sidebar.css +++ b/ui/styles/sidebar.css @@ -24,11 +24,19 @@ color: var(--colour-navbar-active-text); } +.list-nav li.active a { + color: var(--colour-navbar-active-text); +} + .list-nav li:hover { background-color: var(--colour-navbar-hover-bg); color: var(--colour-navbar-hover-text); } +.list-nav li:hover a { + color: var(--colour-navbar-hover-text); +} + /* create channel form */ .create-channel { padding-left: 0.5rem; -- cgit v1.2.3 From ef87bb0719579d55a692992e1843f20e57f209d6 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Mon, 21 Apr 2025 22:00:09 -0400 Subject: Add the following attributes to all markdown-generated links: * `target="_blank"`: when Pilcrow is running in a browser, clicking a link should not replace Pilcrow with the target of the link. Pilcrow is "app-like" enough that opening links in a new tab _by default_, without user intervention, is likely more appropriate. * `rel="noreferrer"`, which (A) stops most UAs from setting a referrer header when following those links, and (B) also implies `noopener`, preventing the link target from using `window.opener` from reaching back into Pilcrow's DOM. I briefly experimented with DOMPurify's `RETURN_DOM_FRAGMENT` mode, which would have made the tests somewhat easier to write, but I wasn't able to find a good way to integrate the returned `DocumentFragment` objects with Svelte components, so HTML-as-strings it is. Sigh. --- ui/lib/markdown.js | 22 +++++++++++++++++++- ui/lib/markdown.test.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 ui/lib/markdown.test.js (limited to 'ui') diff --git a/ui/lib/markdown.js b/ui/lib/markdown.js index 2e73309..c4f2803 100644 --- a/ui/lib/markdown.js +++ b/ui/lib/markdown.js @@ -1,6 +1,26 @@ import { marked } from 'marked'; import DOMPurify from 'dompurify'; +const extension = { + useNewRenderer: true, + renderer: { + link({ title, href, tokens }) { + const titleAttr = title ? ` title="${title}"` : ``; + const text = this.parser.parseInline(tokens); + return `${text}`; + } + } +}; + +marked.use(extension); + export function render(body) { - return DOMPurify.sanitize(marked.parse(body, { breaks: true })); + const rendered = marked.parse(body, { breaks: true }); + return DOMPurify.sanitize(rendered, { + ADD_ATTR: ['target'] + }); } diff --git a/ui/lib/markdown.test.js b/ui/lib/markdown.test.js new file mode 100644 index 0000000..126eacd --- /dev/null +++ b/ui/lib/markdown.test.js @@ -0,0 +1,55 @@ +import * as md from './markdown.js'; +import { expect, describe, it } from 'vitest'; + +describe('render', async () => { + it('renders inline links', async () => { + const markdown = `[a link](https://example.com?foo=bar)`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `

a link

+` + ); + }); + + it('renders inline links with titles', async () => { + const markdown = `[a link](https://example.com?foo=bar "what title")`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `

a link

+` + ); + }); + + it('renders footnote links', async () => { + const markdown = ` +[a link] + +[a link]: https://example.com?foo=bar`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `

a link

+` + ); + }); + + it('renders footnote links with titles', async () => { + const markdown = ` +[a link] + +[a link]: https://example.com?foo=bar "what title"`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `

a link

+` + ); + }); + + it('renders links with embedded markup', async () => { + const markdown = `[a _link_](https://example.com?foo=bar)`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `

a link

+` + ); + }); +}); -- cgit v1.2.3 From 6676c235d260dbadf9f1d62bd17e3cab167de453 Mon Sep 17 00:00:00 2001 From: Kit La Touche Date: Tue, 22 Apr 2025 13:59:01 -0400 Subject: Remove trailing space --- ui/lib/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/lib/markdown.js b/ui/lib/markdown.js index c4f2803..6caf80b 100644 --- a/ui/lib/markdown.js +++ b/ui/lib/markdown.js @@ -7,7 +7,7 @@ const extension = { link({ title, href, tokens }) { const titleAttr = title ? ` title="${title}"` : ``; const text = this.parser.parseInline(tokens); - return `