Fork of Chiri for Astro for my blog
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: embed NeoDB card in post

the3ash 90f53ad9 d53cf66a

+425 -10
+2 -2
src/components/ui/GitHubCard.astro
··· 100 100 101 101 if (!response.ok) { 102 102 if (el.desc) { 103 - el.desc.textContent = '⚠ Loading failed' 103 + el.desc.textContent = '--' 104 104 } 105 105 return 106 106 } ··· 113 113 } catch (error) { 114 114 console.error(`Failed to fetch ${repo}:`, error) 115 115 if (el.desc) { 116 - el.desc.textContent = '⚠ Loading failed' 116 + el.desc.textContent = '--' 117 117 } 118 118 } 119 119 }
+1
src/components/ui/LinkCard.astro
··· 207 207 text-decoration: none; 208 208 color: inherit; 209 209 background: var(--astro-code-background); 210 + min-height: 3.5rem; 210 211 margin: 1.25rem 0 1.75rem 0; 211 212 transition: background 0.2s ease-out; 212 213 }
+379
src/components/ui/NeoDBCard.astro
··· 1 + <script is:inline> 2 + // This script finds all NeoDB containers on the page and fetches data for each one. 3 + document.addEventListener('DOMContentLoaded', () => { 4 + const allContainers = document.querySelectorAll('.neodb-card-container') 5 + 6 + allContainers.forEach((container) => { 7 + if (!container.hasAttribute('data-url')) { 8 + container.remove() 9 + } 10 + }) 11 + 12 + const containers = document.querySelectorAll('.neodb-card-container[data-url]') 13 + 14 + if (containers.length === 0) { 15 + return 16 + } 17 + 18 + const renderError = (elem) => { 19 + elem.innerHTML = `<div class="neodb-card error"><p class="neodb-error">🔴&nbsp;&nbsp;Error</p></div>` 20 + } 21 + 22 + const renderCard = (elem, data, targetUrl) => { 23 + const itemRating = data?.rating && data.rating > 0 ? parseFloat(data.rating) : null 24 + const coverUrl = data.cover_image_url || data.cover_url 25 + const category = data.category || '' 26 + 27 + const getPreferredTitle = () => { 28 + // Special handling for music/album types only 29 + if (category === 'music' || category === 'album') { 30 + if (Array.isArray(data.localized_title)) { 31 + const nonZh = data.localized_title.find((t) => t.lang !== 'zh-cn' && t.text) 32 + if (nonZh) return nonZh.text 33 + const zh = data.localized_title.find((t) => t.lang === 'zh-cn' && t.text) 34 + if (zh) return zh.text 35 + } 36 + } 37 + return data.title || data.name || '' 38 + } 39 + 40 + const title = getPreferredTitle() 41 + 42 + const isSquare = category === 'music' || category === 'podcast' 43 + const coverClass = isSquare ? 'music' : 'other' 44 + const cardHeightClass = isSquare ? 'compact' : 'standard' 45 + 46 + // Round rating to the nearest half-star: 1 point = 0.5 star 47 + const roundedRating = itemRating !== null ? Math.round(itemRating * 2) / 2 : null 48 + 49 + // Build field info 50 + const fieldInfo = [] 51 + 52 + if (category === 'movie' || category === 'tv' || category === 'tv/season') { 53 + // Movie/TV: Director, Cast, Genre, Release Year 54 + if (data.director && data.director.length > 0) { 55 + const directors = Array.isArray(data.director) ? data.director.join(', ') : data.director 56 + fieldInfo.push(`导演: ${directors}`) 57 + } 58 + if (data.actor && data.actor.length > 0) { 59 + const actors = Array.isArray(data.actor) ? data.actor.join(', ') : data.actor 60 + fieldInfo.push(`演员: ${actors}`) 61 + } 62 + if (data.genre && data.genre.length > 0) { 63 + const genres = Array.isArray(data.genre) ? data.genre.join(', ') : data.genre 64 + fieldInfo.push(`类型: ${genres}`) 65 + } 66 + // Add release year - "Release Date" for movies, "Premiere" for TV 67 + if (data.year) { 68 + const timeLabel = category === 'movie' ? '上映时间' : '首播' 69 + fieldInfo.push(`${timeLabel}: ${data.year}`) 70 + } 71 + } else if (category === 'music' || category === 'album') { 72 + // Music: Artist, Genre, Release Date 73 + if (data.artist && data.artist.length > 0) { 74 + // Only show the first artist for music/album 75 + const artist = Array.isArray(data.artist) ? data.artist[0] : data.artist 76 + fieldInfo.push(`艺术家: ${artist}`) 77 + } 78 + if (data.genre && data.genre.length > 0) { 79 + const genres = Array.isArray(data.genre) ? data.genre.join(', ') : data.genre 80 + fieldInfo.push(`流派: ${genres}`) 81 + } 82 + if (data.release_date) { 83 + fieldInfo.push(`发行时间: ${data.release_date}`) 84 + } 85 + } else if (category === 'book') { 86 + // Book: Author, Publisher 87 + if (data.author && data.author.length > 0) { 88 + const authors = Array.isArray(data.author) ? data.author.join(', ') : data.author 89 + fieldInfo.push(`作者: ${authors}`) 90 + } 91 + // Check multiple possible publisher fields 92 + const publisher = data.publisher || data.pub_house || data.company 93 + if (publisher && (Array.isArray(publisher) ? publisher.length > 0 : publisher)) { 94 + const publishers = Array.isArray(publisher) ? publisher.join(', ') : publisher 95 + fieldInfo.push(`出版社: ${publishers}`) 96 + } 97 + } else if (category === 'game') { 98 + // Game: Developer, Genre, Release Date 99 + if (data.developer && data.developer.length > 0) { 100 + const developers = Array.isArray(data.developer) 101 + ? data.developer.join(', ') 102 + : data.developer 103 + fieldInfo.push(`开发者: ${developers}`) 104 + } 105 + if (data.genre && data.genre.length > 0) { 106 + const genres = Array.isArray(data.genre) ? data.genre.join(', ') : data.genre 107 + fieldInfo.push(`类型: ${genres}`) 108 + } 109 + if (data.release_date) { 110 + fieldInfo.push(`发行时间: ${data.release_date}`) 111 + } 112 + } else if (category === 'podcast') { 113 + // Podcast: Host, Genre 114 + const host = data.host || data.artist || data.creator 115 + if (host && (Array.isArray(host) ? host.length > 0 : host)) { 116 + const hosts = Array.isArray(host) ? host.join(', ') : host 117 + fieldInfo.push(`主播: ${hosts}`) 118 + } 119 + if (data.genre && data.genre.length > 0) { 120 + const genres = Array.isArray(data.genre) ? data.genre.join(', ') : data.genre 121 + fieldInfo.push(`类型: ${genres}`) 122 + } 123 + } 124 + 125 + elem.innerHTML = ` 126 + <a class="neodb-card ${cardHeightClass}" href="${targetUrl}" target="_blank" rel="noopener noreferrer"> 127 + ${coverUrl ? `<img class="neodb-cover ${coverClass}" src="${coverUrl}" alt="${title || ''}" loading="lazy" />` : `<div class="neodb-cover ${coverClass}" style="background: #f3f4f6;"></div>`} 128 + <div class="neodb-info"> 129 + <div class="neodb-title">${title || ''}</div> 130 + ${ 131 + roundedRating !== null 132 + ? `<div class="rating"> 133 + <span class="allstarbg"> 134 + <span class="allstarfg" style="width:${(roundedRating / 10) * 100}%"></span> 135 + </span> 136 + <span class="rating_nums">${itemRating.toFixed(1)}</span> 137 + </div>` 138 + : `<div class="rating"> 139 + <span class="allstargray"></span> 140 + </div>` 141 + } 142 + ${fieldInfo 143 + .map((info) => { 144 + if (info.startsWith('<div class="neodb-desc">')) { 145 + return info 146 + } 147 + return `<div class="neodb-field">${info}</div>` 148 + }) 149 + .join('')} 150 + </div> 151 + </a> 152 + ` 153 + } 154 + 155 + containers.forEach((container) => { 156 + const url = container.getAttribute('data-url') 157 + 158 + if (!url) return 159 + 160 + let fetchUrl = '' 161 + const neodbUrlPattern = 162 + /neodb\.social\/(movie|book|music|album|game|tv\/season|tv|podcast)\/([\w-]+)/ 163 + const isNeoDbUrl = neodbUrlPattern.test(url) 164 + 165 + if (isNeoDbUrl) { 166 + const match = url.match(neodbUrlPattern) 167 + let category = match ? match[1] : '' 168 + const uuid = match ? match[2] : '' 169 + 170 + if (category === 'tv/season') { 171 + category = 'tv/season' 172 + } 173 + 174 + if (uuid && category) { 175 + fetchUrl = `https://neodb.social/api/${category}/${uuid}` 176 + } 177 + } else { 178 + fetchUrl = `https://neodb.social/api/catalog/fetch?url=${encodeURIComponent(url)}` 179 + } 180 + 181 + if (fetchUrl) { 182 + fetch(fetchUrl, { 183 + mode: 'cors', 184 + headers: { 185 + Accept: 'application/json' 186 + } 187 + }) 188 + .then((res) => { 189 + if (!res.ok) throw new Error(`Response ${res.status}`) 190 + return res.json() 191 + }) 192 + .then((json) => { 193 + const data = json.data ? json.data : json 194 + renderCard(container, data, url) 195 + }) 196 + .catch(() => { 197 + renderError(container) 198 + }) 199 + } else { 200 + renderError(container) 201 + } 202 + }) 203 + }) 204 + </script> 205 + 206 + <style is:inline> 207 + .prose .neodb-card { 208 + width: 100%; 209 + height: 100%; 210 + background: var(--astro-code-background); 211 + color: var(--text-primary); 212 + border-radius: 8px; 213 + display: flex; 214 + position: relative; 215 + text-decoration: none !important; 216 + transition: background 0.2s ease-out; 217 + margin: 1.25rem 0 1.75rem 0; 218 + overflow: hidden; 219 + } 220 + 221 + .prose .neodb-card.compact { 222 + min-height: 6rem; 223 + } 224 + 225 + .prose .neodb-card.standard { 226 + min-height: 9rem; 227 + } 228 + 229 + .prose .neodb-card:hover { 230 + background: color-mix(in srgb, var(--selection) 75%, transparent); 231 + } 232 + 233 + .prose .neodb-card.error:hover { 234 + background: var(--astro-code-background) !important; 235 + cursor: default; 236 + } 237 + 238 + .prose .neodb-cover { 239 + width: 96px !important; 240 + border-radius: 6px; 241 + margin: 1rem; 242 + } 243 + 244 + .prose .neodb-cover.music { 245 + height: 96px !important; /* 1:1 for music */ 246 + } 247 + 248 + .prose .neodb-cover.other { 249 + height: 144px !important; /* 9:16 for other types */ 250 + } 251 + 252 + .prose .neodb-info { 253 + height: 100%; 254 + display: flex; 255 + flex-direction: column; 256 + gap: 0.25rem; 257 + flex: 1; 258 + min-width: 0; 259 + overflow: hidden; 260 + margin: 1rem 1rem 1rem 0; 261 + } 262 + 263 + .prose .neodb-field { 264 + font-size: var(--font-size-s); 265 + color: var(--text-primary); 266 + line-height: 1.4; 267 + white-space: nowrap; 268 + overflow: hidden; 269 + text-overflow: ellipsis; 270 + } 271 + 272 + .prose .neodb-artist { 273 + font-size: var(--font-size-s); 274 + color: var(--text-primary); 275 + } 276 + 277 + .prose .no-rating { 278 + font-size: var(--font-size-s); 279 + color: var(--text-primary); 280 + line-height: 1; 281 + margin: 0 0 0.25rem 0; 282 + } 283 + 284 + .prose .neodb-title { 285 + color: var(--text-primary); 286 + font-weight: var(--font-weight-bold); 287 + line-height: 1.35; 288 + margin-bottom: 0.125rem; 289 + } 290 + 291 + .prose .neodb-title a { 292 + text-decoration: none !important; 293 + } 294 + 295 + .prose .neodb-desc { 296 + font-size: var(--font-size-s); 297 + color: var(--text-primary); 298 + margin-top: 0.25rem; 299 + display: -webkit-box; 300 + -webkit-line-clamp: 2; 301 + -webkit-box-orient: vertical; 302 + text-overflow: ellipsis; 303 + overflow: hidden; 304 + word-wrap: break-word; 305 + max-width: 100%; 306 + } 307 + 308 + .prose .rating { 309 + margin: 0 0 0.25rem 0; 310 + font-size: var(--font-size-s); 311 + line-height: 1; 312 + display: flex; 313 + align-items: center; 314 + } 315 + 316 + .prose .rating .allstarbg { 317 + position: relative; 318 + color: #f99b01; 319 + height: 1rem; 320 + width: 5rem; 321 + background-size: auto 100%; 322 + margin-right: 0.25rem; 323 + background-repeat: repeat; 324 + background-image: url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjxwYXRoIGQ9Ik05MDguMSAzNTMuMWwtMjUzLjktMzYuOUw1NDAuNyA4Ni4xYy0zLjEtNi4zLTguMi0xMS40LTE0LjUtMTQuNS0xNS44LTcuOC0zNS0xLjMtNDIuOSAxNC41TDM2OS44IDMxNi4ybC0yNTMuOSAzNi45Yy03IDEtMTMuNCA0LjMtMTguMyA5LjMtMTIuMyAxMi43LTEyLjEgMzIuOS42IDQ1LjNsMTgzLjcgMTc5LjEtNDMuNCAyNTIuOWMtMS4yIDYuOS0uMSAxNC4xIDMuMiAyMC4zIDguMiAxNS42IDI3LjYgMjEuNyA0My4yIDEzLjRMNTEyIDc1NGwyMjcuMSAxMTkuNGM2LjIgMy4zIDEzLjQgNC40IDIwLjMgMy4yIDE3LjQtMyAyOS4xLTE5LjUgMjYuMS0zNi45bC00My40LTI1Mi45IDE4My43LTE3OS4xYzUtNC45IDguMy0xMS4zIDkuMy0xOC4zIDIuNy0xNy41LTkuNS0zMy43LTI3LTM2LjN6TTY2NC44IDU2MS42bDM2LjEgMjEwLjNMNTEyIDY3Mi43IDMyMy4xIDc3MmwzNi4xLTIxMC4zLTE1Mi44LTE0OUw0MTcuNiAzODIgNTEyIDE5MC43IDYwNi40IDM4MmwyMTEuMiAzMC43LTE1Mi44IDE0OC45eiIgZmlsbD0iI2Y5OWIwMSIvPjwvc3ZnPg==); 325 + } 326 + 327 + .prose .rating .allstarfg { 328 + position: absolute; 329 + left: 0; 330 + color: #f99b01; 331 + height: 1rem; 332 + overflow: hidden; 333 + background-size: auto 100%; 334 + background-repeat: repeat; 335 + background-image: url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjxwYXRoIGQ9Ik05MDguMSAzNTMuMWwtMjUzLjktMzYuOUw1NDAuNyA4Ni4xYy0zLjEtNi4zLTguMi0xMS40LTE0LjUtMTQuNS0xNS44LTcuOC0zNS0xLjMtNDIuOSAxNC41TDM2OS44IDMxNi4ybC0yNTMuOSAzNi45Yy03IDEtMTMuNCA0LjMtMTguMyA5LjMtMTIuMyAxMi43LTEyLjEgMzIuOS42IDQ1LjNsMTgzLjcgMTc5LjEtNDMuNCAyNTIuOWMtMS4yIDYuOS0uMSAxNC4xIDMuMiAyMC4zIDguMiAxNS42IDI3LjYgMjEuNyA0My4yIDEzLjRMNTEyIDc1NGwyMjcuMSAxMTkuNGM2LjIgMy4zIDEzLjQgNC40IDIwLjMgMy4yIDE3LjQtMyAyOS4xLTE5LjUgMjYuMS0zNi45bC00My40LTI1Mi45IDE4My43LTE3OS4xYzUtNC45IDguMy0xMS4zIDkuMy0xOC4zIDIuNy0xNy41LTkuNS0zMy43LTI3LTM2LjN6IiBmaWxsPSIjZjk5YjAxIi8+PC9zdmc+); 336 + } 337 + 338 + .prose .rating_nums { 339 + font-size: var(--font-size-s); 340 + color: var(--text-primary); 341 + } 342 + 343 + .prose .rating .allstargray { 344 + position: relative; 345 + height: 1rem; 346 + width: 5rem; 347 + margin-right: 8px; 348 + background-color: var(--text-primary); 349 + opacity: 0.15; 350 + -webkit-mask-image: url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjxwYXRoIGQ9Ik05MDguMSAzNTMuMWwtMjUzLjktMzYuOUw1NDAuNyA4Ni4xYy0zLjEtNi4zLTguMi0xMS40LTE0LjUtMTQuNS0xNS44LTcuOC0zNS0xLjMtNDIuOSAxNC41TDM2OS44IDMxNi4ybC0yNTMuOSAzNi45Yy03IDEtMTMuNCA0LjMtMTguMyA5LjMtMTIuMyAxMi43LTEyLjEgMzIuOS42IDQ1LjNsMTgzLjcgMTc5LjEtNDMuNCAyNTIuOWMtMS4yIDYuOS0uMSAxNC4xIDMuMiAyMC4zIDguMiAxNS42IDI3LjYgMjEuNyA0My4yIDEzLjRMNTEyIDc1NGwyMjcuMSAxMTkuNGM2LjIgMy4zIDEzLjQgNC40IDIwLjMgMy4yIDE3LjQtMyAyOS4xLTE5LjUgMjYuMS0zNi45bC00My40LTI1Mi45IDE4My43LTE3OS4xYzUtNC45IDguMy0xMS4zIDkuMy0xOC4zIDIuNy0xNy41LTkuNS0zMy43LTI3LTM2LjN6IiBmaWxsPSIjZmZmZmZmIi8+PC9zdmc+); 351 + mask-image: url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjxwYXRoIGQ9Ik05MDguMSAzNTMuMWwtMjUzLjktMzYuOUw1NDAuNyA4Ni4xYy0zLjEtNi4zLTguMi0xMS40LTE0LjUtMTQuNS0xNS44LTcuOC0zNS0xLjMtNDIuOSAxNC41TDM2OS44IDMxNi4ybC0yNTMuOSAzNi45Yy03IDEtMTMuNCA0LjMtMTguMyA5LjMtMTIuMyAxMi43LTEyLjEgMzIuOS42IDQ1LjNsMTgzLjcgMTc5LjEtNDMuNCAyNTIuOWMtMS4yIDYuOS0uMSAxNC4xIDMuMiAyMC4zIDguMiAxNS42IDI3LjYgMjEuNyA0My4yIDEzLjRMNTEyIDc1NGwyMjcuMSAxMTkuNGM2LjIgMy4zIDEzLjQgNC40IDIwLjMgMy4yIDE3LjQtMyAyOS4xLTE5LjUgMjYuMS0zNi45bC00My40LTI1Mi45IDE4My43LTE3OS4xYzUtNC45IDguMy0xMS4zIDkuMy0xOC4zIDIuNy0xNy41LTkuNS0zMy43LTI3LTM2LjN6IiBmaWxsPSIjZmZmZmZmIi8+PC9zdmc+); 352 + -webkit-mask-size: auto 100%; 353 + mask-size: auto 100%; 354 + -webkit-mask-repeat: repeat; 355 + mask-repeat: repeat; 356 + } 357 + 358 + /* Skeleton/Loading card styles */ 359 + .prose .neodb-loading.music { 360 + min-height: 8rem !important; 361 + background: var(--astro-code-background); 362 + display: flex; 363 + align-items: center; 364 + justify-content: center; 365 + } 366 + 367 + .prose .neodb-loading.other { 368 + min-height: 11rem !important; 369 + background: var(--astro-code-background); 370 + display: flex; 371 + align-items: center; 372 + justify-content: center; 373 + } 374 + 375 + .prose .neodb-error { 376 + font-size: var(--font-size-m); 377 + margin: 1.5rem; 378 + } 379 + </style>
+17 -5
src/content/posts/embedded-content.md
··· 6 6 Use these directives to embed media: 7 7 8 8 ``` 9 - ::link{url="https://xxxxx"} 9 + ::link{url="https://xxxxx.xxx"} 10 10 11 - ::spotify{url="https://open.spotify.com/type/xxxxxx"} 11 + ::spotify{url="https://open.spotify.com/type/xxxxx"} 12 12 13 - ::youtube{url="https://www.youtube.com/watch?v=xxxxxx"} 13 + ::youtube{url="https://www.youtube.com/watch?v=xxxxx"} 14 14 15 - ::bilibili{url="https://www.bilibili.com/video/xxxxxx"} 15 + ::bilibili{url="https://www.bilibili.com/video/xxxxx"} 16 16 17 17 ::github{repo="username/repo"} 18 18 19 - ::x{url="https://x.com/username/status/xxxxxx"} 19 + ::x{url="https://x.com/username/status/xxxxx"} 20 + 21 + ::neodb{url="https://neodb.social/category/xxxxx"} 20 22 ``` 21 23 22 24 ``` ··· 59 61 ## X Post 60 62 61 63 ::x{url="https://x.com/DAVID_LYNCH/status/1174367510893752321"} 64 + 65 + ## NeoDB (CN Only) 66 + 67 + ::neodb{url="https://neodb.social/album/5nD3R8gmnVlsoOBdyO8PA3"} 68 + 69 + ::neodb{url="https://neodb.social/movie/1bhogjXkNnlWWM0bf6aj8P"} 70 + 71 + ::neodb{url="https://neodb.social/book/4BqQ5mhfKMHPND3L6hf0Qh"} 72 + 73 + ::neodb{url="https://neodb.social/game/1hl18l0qD5UN93k8ZkCZ7Q"}
+4 -2
src/layouts/PostLayout.astro
··· 8 8 import BackButton from '@/components/ui/BackButton.astro' 9 9 import TableOfContents from '@/components/ui/TableOfContents.astro' 10 10 import GradientMask from '@/components/ui/GradientMask.astro' 11 + import ImageOptimizer from '@/components/ui/ImageOptimizer.astro' 11 12 import ImageViewer from '@/components/ui/ImageViewer.astro' 12 13 import GitHubCard from '@/components/ui/GitHubCard.astro' 13 14 import LinkCard from '@/components/ui/LinkCard.astro' 14 - import ImageOptimizer from '@/components/ui/ImageOptimizer.astro' 15 + import NeoDBCard from '@/components/ui/NeoDBCard.astro' 15 16 import XPOST from '@/components/ui/XPOST.astro' 16 17 import CopyCode from '@/components/ui/CopyCode.astro' 17 18 import BaseLayout from '@/layouts/BaseLayout.astro' ··· 58 59 <slot /> 59 60 </div> 60 61 </main> 62 + <ImageOptimizer /> 61 63 <FootnoteScroll /> 62 64 <CopyCode /> 63 65 <GitHubCard /> 64 66 <XPOST /> 65 - <ImageOptimizer /> 67 + <NeoDBCard /> 66 68 {themeConfig.post.imageViewer && <ImageViewer />} 67 69 {themeConfig.post.linkCard && <LinkCard />} 68 70 {themeConfig.general.footer && <Footer />}
+22 -1
src/plugins/remark-embedded-media.mjs
··· 173 173 <path d="M12 1C5.9225 1 1 5.9225 1 12C1 16.8675 4.14875 20.9787 8.52125 22.4362C9.07125 22.5325 9.2775 22.2025 9.2775 21.9137C9.2775 21.6525 9.26375 20.7862 9.26375 19.865C6.5 20.3737 5.785 19.1912 5.565 18.5725C5.44125 18.2562 4.905 17.28 4.4375 17.0187C4.0525 16.8125 3.5025 16.3037 4.42375 16.29C5.29 16.2762 5.90875 17.0875 6.115 17.4175C7.105 19.0812 8.68625 18.6137 9.31875 18.325C9.415 17.61 9.70375 17.1287 10.02 16.8537C7.5725 16.5787 5.015 15.63 5.015 11.4225C5.015 10.2262 5.44125 9.23625 6.1425 8.46625C6.0325 8.19125 5.6475 7.06375 6.2525 5.55125C6.2525 5.55125 7.17375 5.2625 9.2775 6.67875C10.1575 6.43125 11.0925 6.3075 12.0275 6.3075C12.9625 6.3075 13.8975 6.43125 14.7775 6.67875C16.8813 5.24875 17.8025 5.55125 17.8025 5.55125C18.4075 7.06375 18.0225 8.19125 17.9125 8.46625C18.6138 9.23625 19.04 10.2125 19.04 11.4225C19.04 15.6437 16.4688 16.5787 14.0213 16.8537C14.42 17.1975 14.7638 17.8575 14.7638 18.8887C14.7638 20.36 14.75 21.5425 14.75 21.9137C14.75 22.2025 14.9563 22.5462 15.5063 22.4362C19.8513 20.9787 23 16.8537 23 12C23 5.9225 18.0775 1 12 1Z"></path> 174 174 </svg> 175 175 </div> 176 - <p class="gc-repo-description">Loading...</p> 176 + <p class="gc-repo-description">--</p> 177 177 <div class="gc-info-bar"> 178 178 <svg class="gc-info-icon" height="16" width="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"> 179 179 <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> ··· 190 190 </div> 191 191 </a> 192 192 ` 193 + }, 194 + 195 + // NeoDB Card 196 + neodb: (node) => { 197 + const url = node.attributes?.url ?? '' 198 + if (!url) { 199 + return false 200 + } 201 + 202 + const neodbUrlPattern = 203 + /neodb\.social\/(movie|book|music|album|game|tv\/season|tv|podcast)\/([\w-]+)/ 204 + const match = url.match(neodbUrlPattern) 205 + const category = match ? match[1] : 'other' 206 + 207 + const isSquare = category === 'music' || category === 'album' || category === 'podcast' 208 + const skeletonClass = isSquare ? 'music' : 'other' 209 + 210 + return `<div class="neodb-card-container" data-url="${url}"> 211 + <div class="neodb-card neodb-loading ${skeletonClass}"> 212 + </div> 213 + </div>` 193 214 } 194 215 } 195 216