2022-07-31 13:10:20 -08:00
|
|
|
<template>
|
2025-09-27 13:57:53 -05:00
|
|
|
<!-- eslint-disable-next-line vue/no-v-html is safe here because all HTML is sanitized with DOMPurify in setup() -->
|
|
|
|
|
<div v-html="value" />
|
2022-07-31 13:10:20 -08:00
|
|
|
</template>
|
|
|
|
|
|
2026-03-23 21:18:25 +01:00
|
|
|
<script setup lang="ts">
|
2025-06-26 21:58:31 +02:00
|
|
|
import { marked } from "marked";
|
2026-05-31 11:14:16 -05:00
|
|
|
import { sanitizeMarkdownHtml } from "~/lib/sanitize/markdown";
|
2026-02-01 16:24:57 -06:00
|
|
|
|
2026-03-23 21:18:25 +01:00
|
|
|
const props = defineProps({
|
|
|
|
|
source: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: "",
|
2022-07-31 13:10:20 -08:00
|
|
|
},
|
2026-03-23 21:18:25 +01:00
|
|
|
});
|
2026-02-01 16:24:57 -06:00
|
|
|
|
2026-05-31 11:14:16 -05:00
|
|
|
const { $appInfo } = useNuxtApp();
|
2025-06-20 00:09:12 +07:00
|
|
|
|
2026-03-23 21:18:25 +01:00
|
|
|
const value = computed(() => {
|
|
|
|
|
const rawHtml = marked.parse(props.source || "", { async: false, breaks: true });
|
2026-05-31 11:14:16 -05:00
|
|
|
return sanitizeMarkdownHtml(rawHtml, $appInfo?.allowedIframeHosts ?? []);
|
2022-07-31 13:10:20 -08:00
|
|
|
});
|
|
|
|
|
</script>
|
2024-12-11 03:33:43 -06:00
|
|
|
|
|
|
|
|
<style scoped>
|
2025-06-20 00:09:12 +07:00
|
|
|
:deep(table) {
|
2024-12-11 03:33:43 -06:00
|
|
|
border-collapse: collapse;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 21:58:31 +02:00
|
|
|
:deep(th),
|
|
|
|
|
:deep(td) {
|
2024-12-11 03:33:43 -06:00
|
|
|
border: 1px solid;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:09:12 +07:00
|
|
|
:deep(th) {
|
2024-12-11 03:33:43 -06:00
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
2025-06-26 21:58:31 +02:00
|
|
|
|
|
|
|
|
:deep(ul),
|
|
|
|
|
:deep(ol) {
|
|
|
|
|
margin: 8px 0;
|
|
|
|
|
padding-left: 20px;
|
|
|
|
|
}
|
2024-12-11 03:33:43 -06:00
|
|
|
</style>
|