Skip to content

Commit f88c213

Browse files
Merge pull request #115 from festim-dev/fix-shared_x
Sharex plotly fig fix + export HTML
2 parents d4fbd13 + e70bfba commit f88c213

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

src/App.jsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const DnDFlow = () => {
4444
const [copyFeedback, setCopyFeedback] = useState('');
4545
const ref = useRef(null);
4646
const [csvData, setCsvData] = useState(null);
47+
const [htmlData, setHtmlData] = useState(null);
4748
const reactFlowWrapper = useRef(null);
4849
// const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
4950
// const [edges, setEdges, onEdgesChange] = useEdgesState([]);
@@ -481,6 +482,39 @@ const DnDFlow = () => {
481482
}
482483
};
483484

485+
const downloadHtml = async () => {
486+
const blob = new Blob([htmlData], { type: "text/html" });
487+
const filename = `simulation_${new Date().toISOString().replace(/[:.]/g, "-")}.html`;
488+
489+
try {
490+
if ("showSaveFilePicker" in window) {
491+
const options = {
492+
suggestedName: filename,
493+
types: [{
494+
description: "HTML File",
495+
accept: { "text/html": [".html"] }
496+
}]
497+
};
498+
499+
const handle = await window.showSaveFilePicker(options);
500+
const writable = await handle.createWritable();
501+
await writable.write(blob);
502+
await writable.close();
503+
} else {
504+
throw new Error("showSaveFilePicker not supported");
505+
}
506+
} catch (err) {
507+
console.warn("Falling back to automatic download:", err);
508+
const a = document.createElement("a");
509+
a.href = URL.createObjectURL(blob);
510+
a.download = filename;
511+
document.body.appendChild(a);
512+
a.click();
513+
document.body.removeChild(a);
514+
URL.revokeObjectURL(a.href);
515+
}
516+
};
517+
484518

485519

486520
// Allows user to save to python script
@@ -581,6 +615,7 @@ const DnDFlow = () => {
581615
// Store results and switch to results tab
582616
setSimulationResults(result.plot);
583617
setCsvData(result.csv_data);
618+
setHtmlData(result.html);
584619
setActiveTab('results');
585620
alert('Pathsim simulation completed successfully! Check the Results tab.');
586621
} else {
@@ -1664,6 +1699,19 @@ const DnDFlow = () => {
16641699
{simulationResults ? (
16651700
<>
16661701
<div style={{ textAlign: 'right', padding: '0 20px 10px 20px' }}>
1702+
<button
1703+
style={{
1704+
backgroundColor: '#78A083',
1705+
color: 'white',
1706+
border: 'none',
1707+
borderRadius: 5,
1708+
cursor: 'pointer',
1709+
marginRight: '10px',
1710+
}}
1711+
onClick={downloadHtml}
1712+
>
1713+
Download HTML
1714+
</button>
16671715
<button
16681716
style={{
16691717
backgroundColor: '#78A083',

src/backend.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,18 @@ def run_pathsim():
264264
# extra work is needed since spectra and scopes don't share the same x axis
265265
csv_payload = make_csv_payload(scopes)
266266

267+
# Share x only if there are only scopes or only spectra
268+
shared_x = len(scopes) * len(spectra) == 0
269+
n_rows = len(scopes) + len(spectra)
270+
absolute_vertical_spacing = 0.05
271+
relative_vertical_spacing = absolute_vertical_spacing / n_rows
267272
fig = make_subplots(
268-
rows=len(scopes) + len(spectra),
273+
rows=n_rows,
269274
cols=1,
270-
shared_xaxes=False,
275+
shared_xaxes=shared_x,
271276
subplot_titles=[scope.label for scope in scopes]
272277
+ [spec.label for spec in spectra],
273-
vertical_spacing=0.2,
278+
vertical_spacing=relative_vertical_spacing,
274279
)
275280

276281
# make scope plots
@@ -302,16 +307,18 @@ def run_pathsim():
302307
fig.update_xaxes(title_text="Frequency", row=len(scopes) + i + 1, col=1)
303308

304309
fig.update_layout(
305-
height=400 * (len(scopes) + len(spectra)), hovermode="x unified"
310+
height=500 * (len(scopes) + len(spectra)), hovermode="x unified"
306311
)
307312

308313
# Convert plot to JSON
309314
plot_data = plotly_json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
315+
plot_html = fig.to_html()
310316

311317
return jsonify(
312318
{
313319
"success": True,
314320
"plot": plot_data,
321+
"html": plot_html,
315322
"csv_data": csv_payload,
316323
"message": "Pathsim simulation completed successfully",
317324
}

0 commit comments

Comments
 (0)