Spaces:
Sleeping
Sleeping
| // Themed Plotly rendering. Figures come from the backend as plain | |
| // {data, layout} JSON; we overlay transparent backgrounds and the current | |
| // theme's font/grid colors, and re-render live plots on theme switch. | |
| // | |
| // IMPORTANT (inherited from miru-tracer): never give a rendered figure a | |
| // fixed width inside a scrollable container — Plotly's responsive resize + | |
| // a toggling scrollbar forms a ResizeObserver feedback loop that freezes | |
| // the browser. Figures stay autosized; wide data pans via the figure's own | |
| // dragmode. | |
| const live = new Map(); // element -> figure JSON | |
| function themeColors() { | |
| const styles = getComputedStyle(document.documentElement); | |
| return { | |
| text: styles.getPropertyValue('--text').trim() || '#e6e9f0', | |
| muted: styles.getPropertyValue('--text-muted').trim() || '#8b93a7', | |
| grid: styles.getPropertyValue('--plot-grid').trim() || 'rgba(127,127,127,0.18)', | |
| font: styles.getPropertyValue('--font-ui').trim() || 'Inter, sans-serif', | |
| }; | |
| } | |
| function themedLayout(layout = {}) { | |
| const c = themeColors(); | |
| const themed = structuredClone(layout); | |
| themed.paper_bgcolor = 'rgba(0,0,0,0)'; | |
| themed.plot_bgcolor = 'rgba(0,0,0,0)'; | |
| themed.font = { ...(themed.font || {}), color: c.text, family: c.font }; | |
| themed.autosize = true; | |
| delete themed.width; // never fixed-width (see module note) | |
| for (const key of Object.keys(themed)) { | |
| if (key.startsWith('xaxis') || key.startsWith('yaxis')) { | |
| themed[key] = { | |
| ...themed[key], | |
| gridcolor: c.grid, zerolinecolor: c.grid, linecolor: c.grid, | |
| tickfont: { ...(themed[key]?.tickfont || {}), color: c.muted }, | |
| }; | |
| } | |
| } | |
| if (themed.legend) themed.legend = { ...themed.legend, font: { color: c.muted } }; | |
| return themed; | |
| } | |
| export function renderPlot(target, fig) { | |
| if (!fig || !fig.data) { clearPlot(target); return; } | |
| live.set(target, fig); | |
| window.Plotly.react(target, fig.data, themedLayout(fig.layout), { | |
| responsive: true, | |
| displaylogo: false, | |
| modeBarButtonsToRemove: ['select2d', 'lasso2d'], | |
| }); | |
| target.classList.add('has-plot'); | |
| } | |
| export function clearPlot(target) { | |
| live.delete(target); | |
| if (target.classList.contains('has-plot')) { | |
| window.Plotly.purge(target); | |
| target.classList.remove('has-plot'); | |
| } | |
| target.replaceChildren(); | |
| } | |
| export function rethemePlots() { | |
| for (const [target, fig] of live) { | |
| if (target.isConnected) renderPlot(target, fig); | |
| else live.delete(target); | |
| } | |
| } | |