Add pretty JSON filter and enhance JSON display in viewer state and annotations

This commit is contained in:
Ross
2026-03-16 11:56:03 +00:00
parent d0686dbe17
commit 3ea51f976d
4 changed files with 120 additions and 4 deletions
@@ -55,12 +55,10 @@
<details>
<summary class="small text-muted" style="cursor:pointer;">Show advanced (viewer state & annotations)</summary>
<div>
<strong>Viewer State:</strong>
<pre class="small">{{ ds.viewerstate|default:"{}" }}</pre>
{% include 'atlas/partials/json_pretty.html' with json_value=ds.viewerstate title='Viewer State' %}
</div>
<div>
<strong>Annotations:</strong>
<pre class="small">{{ ds.annotations|default:"{}" }}</pre>
{% include 'atlas/partials/json_pretty.html' with json_value=ds.annotations title='Annotations' %}
</div>
</details>
</div>
@@ -0,0 +1,78 @@
<div class="json-block mb-2">
<div class="d-flex justify-content-between align-items-center mb-1">
<strong>{{ title }}</strong>
<div>
<button type="button" class="btn btn-sm btn-outline-secondary btn-copy-json" title="Copy JSON">Copy</button>
<button type="button" class="btn btn-sm btn-outline-secondary btn-toggle-json" title="Toggle JSON">Hide</button>
</div>
</div>
<div class="json-container small" style="max-height:360px; overflow:auto; font-family: monospace; background: #f8f9fa; padding: .5rem; border-radius: .25rem;"></div>
<script>
(function(){
// Parse template-provided JSON value into a JS object
var obj;
try{
obj = {{ json_value|default:"{}"|safe }};
}catch(e){
try{ obj = JSON.parse(String({{ json_value|default:"{}"|safe }})); }catch(e){ obj = {}; }
}
var container = document.currentScript.previousElementSibling;
var btnCopy = document.currentScript.parentNode.querySelector('.btn-copy-json');
var btnToggle = document.currentScript.parentNode.querySelector('.btn-toggle-json');
function renderWithFormatter(depth){
container.innerHTML = '';
if (window.JSONFormatter){
try{
var fmt = new JSONFormatter(obj, depth || 1, {hoverPreviewEnabled: false, animateOpen: false});
container.appendChild(fmt.render());
return fmt;
}catch(e){
container.textContent = JSON.stringify(obj, null, 2);
return null;
}
} else {
container.textContent = JSON.stringify(obj, null, 2);
return null;
}
}
// Dynamic load of json-formatter-js if not already present
function ensureFormatter(cb){
if (window.JSONFormatter) return cb();
var s = document.createElement('script');
s.src = 'https://unpkg.com/json-formatter-js@2.3.3/dist/json-formatter.umd.js';
s.onload = function(){ setTimeout(cb, 0); };
s.onerror = function(){ cb(); };
document.head.appendChild(s);
}
// Keep current formatter instance so we can re-render collapsed/expanded
var currentFormatter = null;
ensureFormatter(function(){ currentFormatter = renderWithFormatter(1); });
btnToggle.addEventListener('click', function(){
// Toggle between collapsed (depth=1) and expanded (depth=0 => fully expanded)
if (btnToggle.dataset.state === 'expanded'){
currentFormatter = renderWithFormatter(1);
btnToggle.dataset.state = 'collapsed';
btnToggle.textContent = 'Show';
} else {
currentFormatter = renderWithFormatter(0);
btnToggle.dataset.state = 'expanded';
btnToggle.textContent = 'Hide';
}
});
btnCopy.addEventListener('click', function(){
var text = JSON.stringify(obj, null, 2);
if (navigator.clipboard){ navigator.clipboard.writeText(text).catch(function(){}); }
else { var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); try{document.execCommand('copy');}catch(e){} document.body.removeChild(ta); }
});
})();
</script>
</div>