PLugin for popover information on a line
santiemanuel opened this issue · comments
I'm trying to add a button to the code to show extra info about a specific line of code with the following syntax:
git [options] <command> [args] $#$ "Message for the popover box that will show when I click the button"
I've checked the custom plugin example but I think what I want to do is different since it's an element that potentially will get out side of the code block when opened. This is what I got so far:
I got two issues in here:
- The button doesn't render at the end of the line, but bellow.
- If the text has more than 1 line, it will be cut by the code block.
My current implementation is as follows (aided by Claude and EC documentation):
import { ExpressiveCodeAnnotation } from '@expressive-code/core'
import { h } from 'hastscript'
class MessageBoxAnnotation extends ExpressiveCodeAnnotation {
constructor(options) {
super(options)
this.message = options.message
}
/** @param {import('@expressive-code/core').AnnotationRenderOptions} context */
render() {
return []
}
}
/** @returns {import('@expressive-code/core').ExpressiveCodePlugin} */
export function pluginMessageBox() {
return {
name: 'Message Box',
baseStyles: `
.message-box-button {
appearance: none;
background: none;
border: none;
padding: 0;
margin-left: 0.5rem;
cursor: pointer;
font-size: 1rem;
}
.message-box-popover {
display: block;
position: absolute;
right: 3.5rem;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0.5rem;
margin-top: 0.5rem;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.message-box-popover.show {
display: block;
}
`,
hooks: {
postprocessAnalyzedCode: (context) => {
if (!context.codeBlock.meta.includes('message-box')) return
context.codeBlock.getLines().forEach((line) => {
const match = line.text.match(/\$#\$\s*(.+)$/)
if (match) {
const messageIdx = match.index
const messageEndIdx = match.index + match[0].length
const message = match[1]
line.addAnnotation(
new MessageBoxAnnotation({
inlineRange: {
columnStart: messageIdx,
columnEnd: messageEndIdx,
},
message,
})
)
line.editText(messageIdx, messageEndIdx, '')
}
})
},
postprocessRenderedLine: (context) => {
const annotations = context.line.getAnnotations()
annotations.forEach((annotation) => {
if (annotation instanceof MessageBoxAnnotation) {
const button = h('button.message-box-button', '📝')
const popover = h('div.message-box-popover', annotation.message)
button.properties.onclick = () => {
popover.classList.toggle('show')
}
context.renderData.lineAst.children.push(button)
context.renderData.lineAst.children.push(popover)
}
})
},
},
}
}
I had to move the elements added to the postProcessRenderedLine because the render method from MessageBoxAnnotation
threw an error like the following:
Expected annotation render function to return an array of 0 node(s), but got [{"type":"element","tagName":"button","properties":{"className":["message-box-button"]},"children":[{"type":"text","value":"📝"}]},{"type":"element","tagName":"div","properties":{"className":["message-box-popover"]},"children":[]}].
If there's anything else I could provide please let me know.