diff --git a/src/lib/components/DescriptionRenderer.svelte b/src/lib/components/DescriptionRenderer.svelte index eff84c90..f38dfb07 100644 --- a/src/lib/components/DescriptionRenderer.svelte +++ b/src/lib/components/DescriptionRenderer.svelte @@ -19,7 +19,7 @@ // Apply marks (formatting) if (node.marks) { - node.marks.forEach(mark => { + node.marks.forEach((mark) => { switch (mark.type) { case 'bold': text = `${text}` @@ -79,7 +79,7 @@ return `
${quoteContent}
` case 'codeBlock': - const codeContent = (node.content || []).map(n => n.text || '').join('') + const codeContent = (node.content || []).map((n) => n.text || '').join('') return `
${codeContent}
` case 'hardBreak': @@ -89,9 +89,44 @@ return '
' case 'youtube': - // For now, show a link to the video in truncated view const videoUrl = node.attrs?.src || '' - return `

📹 View Video

` + // Extract video ID from various YouTube URL formats + let videoId = '' + + // Handle different YouTube URL formats + const patterns = [ + /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, + /youtube\.com\/watch\?.*v=([^&\n?#]+)/ + ] + + for (const pattern of patterns) { + const match = videoUrl.match(pattern) + if (match) { + videoId = match[1] + break + } + } + + // If we couldn't extract an ID, fall back to link + if (!videoId) { + return `

📹 View Video

` + } + + // For truncated view, show a link instead of embed + if (truncate) { + return `

📹 View Video

` + } + + // Embed YouTube video with responsive iframe + return `
+ +
` case 'mention': // Handle game item mentions @@ -133,11 +168,7 @@ const parsedHTML = $derived(parseContent(content)) -
+
{@html parsedHTML}
@@ -145,6 +176,7 @@ @use '$src/themes/typography' as *; @use '$src/themes/colors' as *; @use '$src/themes/spacing' as *; + @use '$src/themes/layout' as *; .description-content { color: var(--text-primary); @@ -161,7 +193,9 @@ } } - h1, h2, h3 { + h1, + h2, + h3 { font-weight: $bold; margin: $unit 0 $unit-half 0; } @@ -178,11 +212,13 @@ font-size: $font-medium; } - strong, b { + strong, + b { font-weight: $bold; } - em, i { + em, + i { font-style: italic; } @@ -199,7 +235,7 @@ background: rgba(255, 237, 76, 0.3); color: var(--text-primary); padding: 0 $unit-fourth; - border-radius: 2px; + border-radius: $input-corner; font-weight: $medium; } @@ -213,7 +249,8 @@ } } - ul, ol { + ul, + ol { margin: 0 0 $unit 0; padding-left: $unit-3x; } @@ -225,7 +262,7 @@ code { background: var(--button-bg); padding: 2px $unit-half; - border-radius: 3px; + border-radius: $input-corner; font-family: monospace; font-size: 0.9em; } @@ -233,7 +270,7 @@ pre { background: var(--button-bg); padding: $unit; - border-radius: $unit-half; + border-radius: $card-corner; overflow-x: auto; margin: $unit 0; @@ -256,6 +293,27 @@ border-top: 1px solid var(--button-bg); margin: $unit-2x 0; } + + // Responsive YouTube video embed + .video-wrapper { + position: relative; + padding-bottom: 56.25%; // 16:9 aspect ratio + height: 0; + overflow: hidden; + margin: $unit 0; + border-radius: $card-corner; + background: var(--button-bg); + + iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; + border-radius: $card-corner; + } + } } &.truncate { @@ -267,10 +325,13 @@ // Hide block elements that might break truncation :global { - pre, blockquote, ul, ol { + pre, + blockquote, + ul, + ol { display: inline; } } } } - \ No newline at end of file +