Post {{ post.external_post_id }}
@@ -80,8 +80,8 @@
-
- {{ post.post_title }}
+
+ {{ plainTitle }}
Post {{ post.external_post_id }}
@@ -125,7 +125,7 @@ import { computed, ref } from 'vue'
import { RouterLink } from 'vue-router'
import { usePostsStore } from '../../stores/posts.js'
-import { sanitizeHtml } from '../../utils/htmlSanitize.js'
+import { sanitizeHtml, toPlainText } from '../../utils/htmlSanitize.js'
import PostEmptyThumbs from './PostEmptyThumbs.vue'
import PostImageGrid from './PostImageGrid.vue'
@@ -149,6 +149,10 @@ const merged = computed(() => detail.value || props.post)
const images = computed(() => merged.value.thumbnails || [])
const attachments = computed(() => merged.value.attachments || [])
+// Titles can arrive as stored HTML (e.g. "…"); render as
+// plain text — the CSS makes the title bold.
+const plainTitle = computed(() => toPlainText(props.post.post_title))
+
// Compact-view hero+rail derived from the feed-shape (capped 6).
const hero = computed(() => props.post.thumbnails?.[0])
const rail = computed(() => (props.post.thumbnails || []).slice(1, 4))
@@ -312,7 +316,7 @@ function formatBytes (n) {
.fc-post-card__title {
font-family: 'Fraunces', Georgia, serif;
- font-size: 18px; font-weight: 500;
+ font-size: 18px; font-weight: 700;
margin: 0 0 8px 0;
color: rgb(var(--v-theme-on-surface));
display: -webkit-box;
@@ -369,7 +373,7 @@ function formatBytes (n) {
.fc-post-card__title-full {
font-family: 'Fraunces', Georgia, serif;
font-size: 22px;
- font-weight: 500;
+ font-weight: 700;
margin: 0;
color: rgb(var(--v-theme-on-surface));
}
diff --git a/frontend/src/utils/htmlSanitize.js b/frontend/src/utils/htmlSanitize.js
index 7978fca..5733c52 100644
--- a/frontend/src/utils/htmlSanitize.js
+++ b/frontend/src/utils/htmlSanitize.js
@@ -29,6 +29,17 @@ export function sanitizeHtml (html) {
return doc.body.innerHTML
}
+// Strip all tags + decode entities to plain text. Used for titles that
+// arrive as stored HTML (e.g. "Edelgard at the Sex-Arcade")
+// which must render as text, not literal markup. DOMParser yields an inert
+// document (no script execution, no resource loads), so reading textContent
+// is safe.
+export function toPlainText (html) {
+ if (typeof html !== 'string' || !html) return ''
+ const doc = new DOMParser().parseFromString(html, 'text/html')
+ return (doc.body.textContent || '').trim()
+}
+
function _scrubNode (node) {
const children = Array.from(node.children)
for (const child of children) {