function initVisibility() {
const roleEl = document.querySelector('.role-badge');
if (!roleEl) {
console.log('[Visibility] No role badge found');
return;
}
let docRole = 'public';
if (roleEl.classList.contains('role-internal')) {
docRole = 'internal';
} else if (roleEl.classList.contains('role-developer')) {
docRole = 'developer';
} else if (roleEl.classList.contains('role-public')) {
docRole = 'public';
} else {
const text = roleEl.textContent?.toLowerCase() || '';
if (text.includes('internal')) {
docRole = 'internal';
} else if (text.includes('developer') || text.includes('dev')) {
docRole = 'developer';
}
}
AppState.documentRole = docRole;
AppState.viewingAs = null;
console.log('[Visibility] Document role:', docRole);
if (AppState.documentRole === 'public') {
if (DOM.visibilityRow) {
DOM.visibilityRow.style.display = 'none';
}
return;
}
buildVisibilitySwitcher();
updateVisibilityDisplay();
}
function buildVisibilitySwitcher() {
if (!DOM.visibilityRow) return;
const docRole = AppState.documentRole;
const options = getAvailableVisibilityOptions(docRole);
const roleIcons = {
'internal': '🔒',
'developer': '👨💻',
'public': '📘'
};
DOM.visibilityRow.innerHTML = `
<span class="visibility-label">👁️ View as</span>
<div class="visibility-switcher" id="visibilitySwitcher">
<button class="visibility-current ${docRole}" aria-haspopup="listbox" aria-expanded="false">
<span class="vis-icon">${roleIcons[docRole] || '📘'}</span>
<span class="vis-label">${capitalize(docRole)}</span>
</button>
<div class="visibility-dropdown" role="listbox">
${options.map(opt => `
<button class="visibility-option ${opt}${opt === docRole ? ' selected' : ''}"
data-visibility="${opt}" role="option">
<span class="vis-icon">${roleIcons[opt] || '📘'}</span>
<span>${capitalize(opt)}</span>
</button>
`).join('')}
</div>
</div>
<span class="viewing-counter" id="viewingCounter"></span>
<button class="visibility-reset" id="visibilityReset" style="display: none;">
<span>↩</span> Reset to ${capitalize(docRole)}
</button>
`;
const switcher = DOM.visibilityRow.querySelector('.visibility-switcher');
const currentBtn = switcher.querySelector('.visibility-current');
currentBtn.addEventListener('click', (e) => {
e.stopPropagation();
switcher.classList.toggle('open');
currentBtn.setAttribute('aria-expanded', switcher.classList.contains('open'));
});
switcher.querySelectorAll('.visibility-option').forEach(opt => {
opt.addEventListener('click', () => selectVisibility(opt.dataset.visibility));
});
const resetBtn = DOM.visibilityRow.querySelector('.visibility-reset');
if (resetBtn) {
resetBtn.addEventListener('click', resetVisibility);
}
document.addEventListener('click', (e) => {
if (!switcher.contains(e.target)) {
switcher.classList.remove('open');
currentBtn.setAttribute('aria-expanded', 'false');
}
});
}
function getAvailableVisibilityOptions(role) {
switch (role) {
case 'internal':
return ['internal', 'developer', 'public'];
case 'developer':
return ['developer', 'public'];
case 'public':
return ['public'];
default:
return ['public'];
}
}
function selectVisibility(visibility) {
const docRole = AppState.documentRole;
AppState.viewingAs = (visibility === docRole) ? null : visibility;
AppState.activeCategory = null;
AppState.activeTag = null;
hideDetail();
const switcher = DOM.visibilityRow?.querySelector('.visibility-switcher');
if (switcher) {
switcher.classList.remove('open');
}
updateVisibilityDisplay();
filterErrors();
renderResults();
updateSidebar();
}
function updateVisibilityDisplay() {
if (!DOM.visibilityRow) return;
const switcher = DOM.visibilityRow.querySelector('.visibility-switcher');
if (!switcher) return;
const currentBtn = switcher.querySelector('.visibility-current');
const effective = getEffectiveVisibility();
const roleIcons = {
'internal': '🔒',
'developer': '👨💻',
'public': '📘'
};
currentBtn.className = `visibility-current ${effective}`;
currentBtn.innerHTML = `
<span class="vis-icon">${roleIcons[effective] || '📘'}</span>
<span class="vis-label">${capitalize(effective)}</span>
`;
switcher.querySelectorAll('.visibility-option').forEach(opt => {
opt.classList.toggle('selected', opt.dataset.visibility === effective);
});
updateViewingCounter();
const resetBtn = DOM.visibilityRow.querySelector('.visibility-reset');
if (resetBtn) {
resetBtn.style.display = AppState.viewingAs ? 'inline-flex' : 'none';
}
}
function updateViewingCounter() {
const counter = document.getElementById('viewingCounter');
if (!counter) return;
const visible = AppState.filteredErrors.length;
const total = AppState.errors.length;
counter.innerHTML = `Viewing <strong>${visible}</strong> of <strong>${total}</strong> ${plural(total, 'error')}`;
}
function resetVisibility() {
AppState.viewingAs = null;
AppState.activeCategory = null;
AppState.activeTag = null;
hideDetail();
updateVisibilityDisplay();
filterErrors();
renderResults();
updateSidebar();
}
function capitalize(str) {
if (!str) return '';
return str.charAt(0).toUpperCase() + str.slice(1);
}