/**
* Document Outline Panel
* Parses markdown headings and renders a navigable tree in the sidebar.
*/
function renderOutlinePanel(container, { getEditorContent, getActiveLine, onHeadingClick }) {
container.innerHTML = `
`;
const listEl = document.getElementById('outline-list');
let headings = [];
let activeLine = 1;
let debounceTimer = null;
function parseHeadings(content) {
const result = [];
if (!content) return result;
const lines = content.split('\n');
const regex = /^(#{1,6})\s+(.+)$/;
for (let i = 0; i < lines.length; i++) {
const match = regex.exec(lines[i]);
if (match) {
result.push({
level: match[1].length,
text: match[2].trim(),
line: i + 1
});
}
}
return result;
}
function findActiveHeading(currentLine) {
let active = null;
for (const h of headings) {
if (h.line <= currentLine) {
active = h;
} else {
break;
}
}
return active;
}
function renderHeadings() {
const content = getEditorContent();
headings = parseHeadings(content);
activeLine = getActiveLine();
if (headings.length === 0) {
listEl.innerHTML = `
No headings found
# Heading 1
## Heading 2
### Heading 3
`;
return;
}
const activeHeading = findActiveHeading(activeLine);
listEl.innerHTML = headings.map((h, idx) => `
${escapeHtml(h.text)}
H${h.level}
`).join('') + ``;
listEl.querySelectorAll('.outline-item').forEach(item => {
item.addEventListener('click', () => {
const line = parseInt(item.dataset.line, 10);
onHeadingClick(line);
});
});
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function refresh() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(renderHeadings, 300);
}
function setActiveHeading(line) {
activeLine = line;
const activeHeading = findActiveHeading(line);
listEl.querySelectorAll('.outline-item').forEach(item => {
const itemLine = parseInt(item.dataset.line, 10);
if (activeHeading && itemLine === activeHeading.line) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
}
container._refreshOutline = refresh;
container._setActiveHeading = setActiveHeading;
renderHeadings();
}
module.exports = { renderOutlinePanel };