1 import { visit } from 'unist-util-visit'
2 import { SITE } from '../config.ts'
3
4 /**
5 * Rehype plugin that processes images in markdown content:
6 * - Wraps images with alt text in figure/figcaption elements
7 * - Adds data-preview attribute for image viewer functionality
8 * - Handles multiple images in a single paragraph
9 */
10 export default function rehypeImageProcessor() {
11 return (tree) => {
12 visit(tree, 'element', (node, index, parent) => {
13 if (node.tagName !== 'p') {
14 return
15 }
16 if (!parent || typeof index !== 'number') {
17 return
18 }
19
20 const imgNodes = []
21 let hasNonImageContent = false
22
23 for (const child of node.children) {
24 if (child.type === 'element' && child.tagName === 'img') {
25 imgNodes.push(child)
26 } else if (child.type !== 'text' || child.value.trim() !== '') {
27 hasNonImageContent = true
28 }
29 }
30
31 if (hasNonImageContent || imgNodes.length === 0) {
32 return
33 }
34
35 const newNodes = []
36
37 for (const imgNode of imgNodes) {
38 const alt = imgNode.properties?.alt?.trim()
39
40 imgNode.properties = {
41 ...imgNode.properties,
42 'data-preview': SITE.imageViewer ? 'true' : 'false'
43 }
44
45 if (!alt || alt.includes('_')) {
46 newNodes.push(imgNode)
47 continue
48 }
49
50 const figure = {
51 type: 'element',
52 tagName: 'figure',
53 properties: {
54 className: ['image-caption-wrapper']
55 },
56 children: [
57 imgNode,
58 {
59 type: 'element',
60 tagName: 'figcaption',
61 properties: {
62 className: ['img-caption']
63 },
64 children: [
65 {
66 type: 'text',
67 value: alt
68 }
69 ]
70 }
71 ]
72 }
73
74 newNodes.push(figure)
75 }
76
77 if (newNodes.length > 0) {
78 parent.children.splice(index, 1, ...newNodes)
79 return index + newNodes.length - 1
80 }
81 })
82 }
83 }