Add Youtube video rendering to DescriptionRenderer
This commit is contained in:
parent
a1eef8c8a5
commit
06a91bd532
1 changed files with 79 additions and 18 deletions
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
// Apply marks (formatting)
|
// Apply marks (formatting)
|
||||||
if (node.marks) {
|
if (node.marks) {
|
||||||
node.marks.forEach(mark => {
|
node.marks.forEach((mark) => {
|
||||||
switch (mark.type) {
|
switch (mark.type) {
|
||||||
case 'bold':
|
case 'bold':
|
||||||
text = `<strong>${text}</strong>`
|
text = `<strong>${text}</strong>`
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
return `<blockquote>${quoteContent}</blockquote>`
|
return `<blockquote>${quoteContent}</blockquote>`
|
||||||
|
|
||||||
case 'codeBlock':
|
case 'codeBlock':
|
||||||
const codeContent = (node.content || []).map(n => n.text || '').join('')
|
const codeContent = (node.content || []).map((n) => n.text || '').join('')
|
||||||
return `<pre><code>${codeContent}</code></pre>`
|
return `<pre><code>${codeContent}</code></pre>`
|
||||||
|
|
||||||
case 'hardBreak':
|
case 'hardBreak':
|
||||||
|
|
@ -89,9 +89,44 @@
|
||||||
return '<hr>'
|
return '<hr>'
|
||||||
|
|
||||||
case 'youtube':
|
case 'youtube':
|
||||||
// For now, show a link to the video in truncated view
|
|
||||||
const videoUrl = node.attrs?.src || ''
|
const videoUrl = node.attrs?.src || ''
|
||||||
return `<p><a href="${videoUrl}" target="_blank" rel="noopener noreferrer">📹 View Video</a></p>`
|
// 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 `<p><a href="${videoUrl}" target="_blank" rel="noopener noreferrer">📹 View Video</a></p>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// For truncated view, show a link instead of embed
|
||||||
|
if (truncate) {
|
||||||
|
return `<p><a href="${videoUrl}" target="_blank" rel="noopener noreferrer">📹 View Video</a></p>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embed YouTube video with responsive iframe
|
||||||
|
return `<div class="video-wrapper">
|
||||||
|
<iframe
|
||||||
|
src="https://www.youtube.com/embed/${videoId}"
|
||||||
|
title="YouTube video"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
</div>`
|
||||||
|
|
||||||
case 'mention':
|
case 'mention':
|
||||||
// Handle game item mentions
|
// Handle game item mentions
|
||||||
|
|
@ -133,11 +168,7 @@
|
||||||
const parsedHTML = $derived(parseContent(content))
|
const parsedHTML = $derived(parseContent(content))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="description-content" class:truncate style={truncate ? `--max-lines: ${maxLines}` : ''}>
|
||||||
class="description-content"
|
|
||||||
class:truncate
|
|
||||||
style={truncate ? `--max-lines: ${maxLines}` : ''}
|
|
||||||
>
|
|
||||||
{@html parsedHTML}
|
{@html parsedHTML}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -145,6 +176,7 @@
|
||||||
@use '$src/themes/typography' as *;
|
@use '$src/themes/typography' as *;
|
||||||
@use '$src/themes/colors' as *;
|
@use '$src/themes/colors' as *;
|
||||||
@use '$src/themes/spacing' as *;
|
@use '$src/themes/spacing' as *;
|
||||||
|
@use '$src/themes/layout' as *;
|
||||||
|
|
||||||
.description-content {
|
.description-content {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
|
@ -161,7 +193,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
font-weight: $bold;
|
font-weight: $bold;
|
||||||
margin: $unit 0 $unit-half 0;
|
margin: $unit 0 $unit-half 0;
|
||||||
}
|
}
|
||||||
|
|
@ -178,11 +212,13 @@
|
||||||
font-size: $font-medium;
|
font-size: $font-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
strong, b {
|
strong,
|
||||||
|
b {
|
||||||
font-weight: $bold;
|
font-weight: $bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
em, i {
|
em,
|
||||||
|
i {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,7 +235,7 @@
|
||||||
background: rgba(255, 237, 76, 0.3);
|
background: rgba(255, 237, 76, 0.3);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
padding: 0 $unit-fourth;
|
padding: 0 $unit-fourth;
|
||||||
border-radius: 2px;
|
border-radius: $input-corner;
|
||||||
font-weight: $medium;
|
font-weight: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +249,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul, ol {
|
ul,
|
||||||
|
ol {
|
||||||
margin: 0 0 $unit 0;
|
margin: 0 0 $unit 0;
|
||||||
padding-left: $unit-3x;
|
padding-left: $unit-3x;
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +262,7 @@
|
||||||
code {
|
code {
|
||||||
background: var(--button-bg);
|
background: var(--button-bg);
|
||||||
padding: 2px $unit-half;
|
padding: 2px $unit-half;
|
||||||
border-radius: 3px;
|
border-radius: $input-corner;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +270,7 @@
|
||||||
pre {
|
pre {
|
||||||
background: var(--button-bg);
|
background: var(--button-bg);
|
||||||
padding: $unit;
|
padding: $unit;
|
||||||
border-radius: $unit-half;
|
border-radius: $card-corner;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin: $unit 0;
|
margin: $unit 0;
|
||||||
|
|
||||||
|
|
@ -256,6 +293,27 @@
|
||||||
border-top: 1px solid var(--button-bg);
|
border-top: 1px solid var(--button-bg);
|
||||||
margin: $unit-2x 0;
|
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 {
|
&.truncate {
|
||||||
|
|
@ -267,10 +325,13 @@
|
||||||
|
|
||||||
// Hide block elements that might break truncation
|
// Hide block elements that might break truncation
|
||||||
:global {
|
:global {
|
||||||
pre, blockquote, ul, ol {
|
pre,
|
||||||
|
blockquote,
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue