+7
-5
frontend/src/lib/components/RichText.svelte
+7
-5
frontend/src/lib/components/RichText.svelte
···
5
5
* supports:
6
6
* - bare URLs: https://example.com -> clickable link
7
7
* - markdown links: [text](https://example.com) -> "text" as clickable link
8
+
* - domain/path URLs: github.com/user/repo -> clickable link
8
9
*/
9
10
10
11
interface Props {
···
23
24
function parseText(input: string): TextPart[] {
24
25
const parts: TextPart[] = [];
25
26
26
-
// combined regex: markdown links OR bare URLs
27
+
// combined regex: markdown links OR bare URLs OR domain/path
27
28
// markdown: [text](url)
28
29
// bare URL: https?://... or www....
30
+
// domain/path: github.com/... (must have path to avoid false positives)
29
31
const combinedRegex =
30
-
/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)|(https?:\/\/[^\s<>)\]]+|www\.[^\s<>)\]]+)/gi;
32
+
/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)|(https?:\/\/[^\s<>)\]]+|www\.[^\s<>)\]]+|[a-z0-9][-a-z0-9]*\.[a-z]{2,}\/[^\s<>)\]]+)/gi;
31
33
32
34
let lastIndex = 0;
33
35
let match;
···
49
51
href: match[2]
50
52
});
51
53
} else if (match[3]) {
52
-
// bare URL
54
+
// bare URL or domain/path
53
55
let href = match[3];
54
-
if (href.startsWith('www.')) {
56
+
if (!href.startsWith('http://') && !href.startsWith('https://')) {
55
57
href = 'https://' + href;
56
58
}
57
59
parts.push({
···
79
81
</script>
80
82
81
83
<span class={className}
82
-
>{#each parsed as part}{#if part.type === 'link'}<a
84
+
>{#each parsed as part, i (i)}{#if part.type === 'link'}<a
83
85
href={part.href}
84
86
target="_blank"
85
87
rel="noopener noreferrer"