Skip to content

Commit f5ae776

Browse files
authored
fix: handle html entities in playground description (#591)
1 parent 700cf22 commit f5ae776

3 files changed

Lines changed: 24 additions & 4 deletions

File tree

app/components/MarkdownText.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import { decodeHtmlEntities } from '~/utils/formatters'
3+
24
const props = defineProps<{
35
text: string
46
/** When true, renders link text without the anchor tag (useful when inside another link) */
@@ -21,8 +23,11 @@ function stripMarkdownImages(text: string): string {
2123
2224
// Strip HTML tags and escape remaining HTML to prevent XSS
2325
function stripAndEscapeHtml(text: string): string {
24-
// First strip markdown image badges
25-
let stripped = stripMarkdownImages(text)
26+
// First decode any HTML entities in the input
27+
let stripped = decodeHtmlEntities(text)
28+
29+
// Then strip markdown image badges
30+
stripped = stripMarkdownImages(stripped)
2631
2732
// Then strip actual HTML tags (keep their text content)
2833
// Only match tags that start with a letter or / (to avoid matching things like "a < b > c")

app/components/PackagePlaygrounds.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import type { PlaygroundLink } from '#shared/types'
3+
import { decodeHtmlEntities } from '~/utils/formatters'
34
45
const props = defineProps<{
56
links: PlaygroundLink[]
@@ -126,7 +127,7 @@ function focusMenuItem(index: number) {
126127
:class="[getIcon(firstLink.provider), getColor(firstLink.provider), 'w-4 h-4 shrink-0']"
127128
aria-hidden="true"
128129
/>
129-
<span class="truncate text-fg-muted">{{ firstLink.label }}</span>
130+
<span class="truncate text-fg-muted">{{ decodeHtmlEntities(firstLink.label) }}</span>
130131
</a>
131132
</AppTooltip>
132133

@@ -182,7 +183,7 @@ function focusMenuItem(index: number) {
182183
:class="[getIcon(link.provider), getColor(link.provider), 'w-4 h-4 shrink-0']"
183184
aria-hidden="true"
184185
/>
185-
<span class="truncate">{{ link.label }}</span>
186+
<span class="truncate">{{ decodeHtmlEntities(link.label) }}</span>
186187
</a>
187188
</AppTooltip>
188189
</div>

app/utils/formatters.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ export function toIsoDateString(date: Date): string {
55
return `${year}-${month}-${day}`
66
}
77

8+
const htmlEntities: Record<string, string> = {
9+
'&amp;': '&',
10+
'&lt;': '<',
11+
'&gt;': '>',
12+
'&quot;': '"',
13+
'&#39;': "'",
14+
'&apos;': "'",
15+
'&nbsp;': ' ',
16+
}
17+
18+
export function decodeHtmlEntities(text: string): string {
19+
return text.replace(/&(?:amp|lt|gt|quot|apos|nbsp|#39);/g, match => htmlEntities[match] || match)
20+
}
21+
822
export function formatCompactNumber(
923
value: number,
1024
options?: { decimals?: number; space?: boolean },

0 commit comments

Comments
 (0)