zhou zhou
昨天 46d872c1a5b77aa8799de4a64888a0a24a1422d6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import hljs from 'highlight.js'
function highlightCode(block) {
  hljs.highlightElement(block)
}
function insertLineNumbers(block) {
  const lines = block.innerHTML.split('\n')
  const numberedLines = lines
    .map((line, index) => {
      return `<span class="line-number">${index + 1}</span> ${line}`
    })
    .join('\n')
  block.innerHTML = numberedLines
}
function addCopyButton(block) {
  const copyButton = document.createElement('i')
  copyButton.className = 'copy-button'
  copyButton.innerHTML =
    '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path fill="currentColor" d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1 1 0 0 1 3 21l.003-14c0-.552.45-1 1.006-1zM5.002 8L5 20h10V8zM9 6h8v10h2V4H9z"/></svg>'
  copyButton.onclick = () => {
    const codeContent = block.innerText.replace(/^\d+\s+/gm, '')
    navigator.clipboard.writeText(codeContent).then(() => {
      ElMessage.success('复制成功')
    })
  }
  const preElement = block.parentElement
  if (preElement) {
    let codeWrapper
    if (!block.parentElement.classList.contains('code-wrapper')) {
      codeWrapper = document.createElement('div')
      codeWrapper.className = 'code-wrapper'
      preElement.replaceChild(codeWrapper, block)
      codeWrapper.appendChild(block)
    } else {
      codeWrapper = block.parentElement
    }
    preElement.appendChild(copyButton)
  }
}
function isBlockProcessed(block) {
  return (
    block.hasAttribute('data-highlighted') ||
    !!block.querySelector('.line-number') ||
    !!block.parentElement?.querySelector('.copy-button')
  )
}
function markBlockAsProcessed(block) {
  block.setAttribute('data-highlighted', 'true')
}
function processBlock(block) {
  if (isBlockProcessed(block)) {
    return
  }
  try {
    highlightCode(block)
    insertLineNumbers(block)
    addCopyButton(block)
    markBlockAsProcessed(block)
  } catch (error) {
    console.warn('处理代码块时出错:', error)
  }
}
function processAllCodeBlocks(el) {
  const blocks = Array.from(el.querySelectorAll('pre code'))
  const unprocessedBlocks = blocks.filter((block) => !isBlockProcessed(block))
  if (unprocessedBlocks.length === 0) {
    return
  }
  if (unprocessedBlocks.length <= 10) {
    unprocessedBlocks.forEach((block) => processBlock(block))
  } else {
    const batchSize = 10
    let currentIndex = 0
    const processBatch = () => {
      const batch = unprocessedBlocks.slice(currentIndex, currentIndex + batchSize)
      batch.forEach((block) => {
        processBlock(block)
      })
      currentIndex += batchSize
      if (currentIndex < unprocessedBlocks.length) {
        requestAnimationFrame(processBatch)
      }
    }
    processBatch()
  }
}
function retryProcessing(el, maxRetries = 3, delay = 200) {
  let retryCount = 0
  const tryProcess = () => {
    processAllCodeBlocks(el)
    const remainingBlocks = Array.from(el.querySelectorAll('pre code')).filter(
      (block) => !isBlockProcessed(block)
    )
    if (remainingBlocks.length > 0 && retryCount < maxRetries) {
      retryCount++
      setTimeout(tryProcess, delay * retryCount)
    }
  }
  tryProcess()
}
const highlightDirective = {
  mounted(el) {
    processAllCodeBlocks(el)
    setTimeout(() => {
      retryProcessing(el)
    }, 100)
    const observer = new MutationObserver((mutations) => {
      let hasNewCodeBlocks = false
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          mutation.addedNodes.forEach((node) => {
            if (node.nodeType === Node.ELEMENT_NODE) {
              const element = node
              if (element.tagName === 'PRE' || element.querySelector('pre code')) {
                hasNewCodeBlocks = true
              }
            }
          })
        }
      })
      if (hasNewCodeBlocks) {
        setTimeout(() => {
          processAllCodeBlocks(el)
        }, 50)
      }
    })
    observer.observe(el, {
      childList: true,
      subtree: true
    })
    el._highlightObserver = observer
  },
  updated(el) {
    setTimeout(() => {
      processAllCodeBlocks(el)
    }, 50)
  },
  unmounted(el) {
    const observer = el._highlightObserver
    if (observer) {
      observer.disconnect()
      delete el._highlightObserver
    }
  }
}
function setupHighlightDirective(app) {
  app.directive('highlight', highlightDirective)
}
export { setupHighlightDirective }