better-rtplot 0.4.15__tar.gz → 0.4.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/PKG-INFO +6 -5
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/README.md +5 -4
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/pyproject.toml +1 -1
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/client.py +23 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/server_browser.py +72 -36
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/index.html +21 -6
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/LICENSE +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/interactive_test.py +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/saved_plots/.gitignore +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/server.py +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/server_browser_gui.py +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/auto-render.min.js +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/katex.min.css +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/katex.min.js +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/uPlot.iife.min.js +0 -0
- {better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/uPlot.min.css +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: better-rtplot
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.16
|
|
4
4
|
Summary:
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: jmontp
|
|
@@ -67,10 +67,11 @@ Sender and server don't have to be on the same machine — see the
|
|
|
67
67
|
No Python needed on the viewing machine. On Windows the binary opens
|
|
68
68
|
a small Tk status window showing the listening URL.
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
the
|
|
73
|
-
[multi-column example](examples/05_multi_column/)
|
|
70
|
+
Set columns per row in your sender's layout with
|
|
71
|
+
`PlotRow([Plot(names=["a"]), Plot(names=["b"])], columns=2)`.
|
|
72
|
+
Start the server normally; each row can use a different column count.
|
|
73
|
+
See the [multi-column example](examples/05_multi_column/) for typed and
|
|
74
|
+
dict forms, including a layout with two, one, and three columns.
|
|
74
75
|
|
|
75
76
|
**Client** — pip install in the env that runs your script:
|
|
76
77
|
|
|
@@ -43,10 +43,11 @@ Sender and server don't have to be on the same machine — see the
|
|
|
43
43
|
No Python needed on the viewing machine. On Windows the binary opens
|
|
44
44
|
a small Tk status window showing the listening URL.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
the
|
|
49
|
-
[multi-column example](examples/05_multi_column/)
|
|
46
|
+
Set columns per row in your sender's layout with
|
|
47
|
+
`PlotRow([Plot(names=["a"]), Plot(names=["b"])], columns=2)`.
|
|
48
|
+
Start the server normally; each row can use a different column count.
|
|
49
|
+
See the [multi-column example](examples/05_multi_column/) for typed and
|
|
50
|
+
dict forms, including a layout with two, one, and three columns.
|
|
50
51
|
|
|
51
52
|
**Client** — pip install in the env that runs your script:
|
|
52
53
|
|
|
@@ -164,6 +164,28 @@ class Plot:
|
|
|
164
164
|
})
|
|
165
165
|
|
|
166
166
|
|
|
167
|
+
@dataclass
|
|
168
|
+
class PlotRow:
|
|
169
|
+
"""A group of plots with its own column count in the browser."""
|
|
170
|
+
plots: List[Union[Plot, dict]]
|
|
171
|
+
columns: int
|
|
172
|
+
|
|
173
|
+
def to_dict(self):
|
|
174
|
+
if isinstance(self.columns, bool) or not isinstance(self.columns, int) or self.columns < 1:
|
|
175
|
+
raise ValueError("PlotRow.columns must be a positive integer")
|
|
176
|
+
if not self.plots:
|
|
177
|
+
raise ValueError("PlotRow.plots must contain at least one plot")
|
|
178
|
+
plots = [
|
|
179
|
+
p.to_dict() if hasattr(p, "to_dict") else p
|
|
180
|
+
for p in self.plots
|
|
181
|
+
]
|
|
182
|
+
if any(not isinstance(p, dict) or "names" not in p
|
|
183
|
+
or any(k in p for k in ("plots", "controls", "non_plot_labels"))
|
|
184
|
+
for p in plots):
|
|
185
|
+
raise ValueError("PlotRow can contain only plot descriptions")
|
|
186
|
+
return {"columns": self.columns, "plots": plots}
|
|
187
|
+
|
|
188
|
+
|
|
167
189
|
@dataclass
|
|
168
190
|
class Button:
|
|
169
191
|
id: str
|
|
@@ -455,6 +477,7 @@ def initialize_plots(plot_descriptions=1, handshake_timeout=2.0):
|
|
|
455
477
|
- list of str: one plot, one trace per name
|
|
456
478
|
- list of list of str: one plot per sublist
|
|
457
479
|
- list of dict: multiple plots, each with full styling
|
|
480
|
+
- PlotRow or {"plots": [...], "columns": N}: a row with N columns
|
|
458
481
|
handshake_timeout:
|
|
459
482
|
Max seconds to wait for the config_ack. On timeout we print a
|
|
460
483
|
warning and return (so an old server without the handshake
|
|
@@ -509,34 +509,53 @@ def save_persisted_tabs():
|
|
|
509
509
|
# Config parsing (per tab) #
|
|
510
510
|
###############################
|
|
511
511
|
|
|
512
|
+
def _expand_config(config):
|
|
513
|
+
"""Flatten plots in wire order while retaining explicit row boundaries."""
|
|
514
|
+
plots, controls, layout = [], [], []
|
|
515
|
+
for key, entry in config.items():
|
|
516
|
+
if "non_plot_labels" in entry:
|
|
517
|
+
continue
|
|
518
|
+
if "plots" in entry:
|
|
519
|
+
columns = entry.get("columns")
|
|
520
|
+
children = entry["plots"]
|
|
521
|
+
if isinstance(columns, bool) or not isinstance(columns, int) or columns < 1:
|
|
522
|
+
raise ValueError("Plot row columns must be a positive integer")
|
|
523
|
+
if not isinstance(children, list) or not children:
|
|
524
|
+
raise ValueError("Plot row plots must be a non-empty list")
|
|
525
|
+
indices = []
|
|
526
|
+
for i, child in enumerate(children):
|
|
527
|
+
if (not isinstance(child, dict) or "names" not in child
|
|
528
|
+
or any(k in child for k in ("plots", "controls", "non_plot_labels"))):
|
|
529
|
+
raise ValueError("Plot rows can contain only plot descriptions")
|
|
530
|
+
indices.append(len(plots))
|
|
531
|
+
plots.append((f"{key}/{i}", child))
|
|
532
|
+
layout.append({"kind": "row", "columns": columns, "plots": indices})
|
|
533
|
+
elif "controls" in entry:
|
|
534
|
+
layout.append({"kind": "controls", "index": len(controls)})
|
|
535
|
+
controls.append(entry["controls"])
|
|
536
|
+
else:
|
|
537
|
+
layout.append({"kind": "plot", "index": len(plots)})
|
|
538
|
+
plots.append((key, entry))
|
|
539
|
+
return plots, controls, layout
|
|
540
|
+
|
|
541
|
+
|
|
512
542
|
def parse_config(tab: Tab, json_config):
|
|
513
543
|
"""Translate a client plot config into the tab's buffer/trace metadata."""
|
|
514
544
|
traces_per_plot = []
|
|
515
545
|
trace_info = []
|
|
516
|
-
control_rows = []
|
|
517
546
|
slider_values = {}
|
|
518
547
|
text_values = {}
|
|
519
|
-
layout =
|
|
548
|
+
plot_entries, control_rows, layout = _expand_config(json_config)
|
|
520
549
|
num_datapoints_in_plot = DEFAULT_NUM_DATAPOINTS_IN_PLOT
|
|
521
550
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if "controls" in plot_description:
|
|
530
|
-
row = plot_description["controls"]
|
|
531
|
-
for element in row:
|
|
532
|
-
if element.get("type") in ("slider", "dial") and "value" in element:
|
|
533
|
-
slider_values[element["id"]] = float(element["value"])
|
|
534
|
-
elif element.get("type") == "text_input":
|
|
535
|
-
text_values[element["id"]] = str(element.get("value", ""))
|
|
536
|
-
layout.append({"kind": "controls", "index": len(control_rows)})
|
|
537
|
-
control_rows.append(row)
|
|
538
|
-
continue
|
|
551
|
+
for row in control_rows:
|
|
552
|
+
for element in row:
|
|
553
|
+
if element.get("type") in ("slider", "dial") and "value" in element:
|
|
554
|
+
slider_values[element["id"]] = float(element["value"])
|
|
555
|
+
elif element.get("type") == "text_input":
|
|
556
|
+
text_values[element["id"]] = str(element.get("value", ""))
|
|
539
557
|
|
|
558
|
+
for plot_counter, (_, plot_description) in enumerate(plot_entries):
|
|
540
559
|
trace_names = plot_description["names"]
|
|
541
560
|
traces_per_plot.append(len(trace_names))
|
|
542
561
|
|
|
@@ -546,9 +565,6 @@ def parse_config(tab: Tab, json_config):
|
|
|
546
565
|
for name in trace_names:
|
|
547
566
|
trace_info.append((name, plot_counter))
|
|
548
567
|
|
|
549
|
-
layout.append({"kind": "plot", "index": plot_counter})
|
|
550
|
-
plot_counter += 1
|
|
551
|
-
|
|
552
568
|
tab.traces_per_plot = traces_per_plot
|
|
553
569
|
tab.trace_labels = trace_info
|
|
554
570
|
tab.control_rows = control_rows
|
|
@@ -589,11 +605,7 @@ def parse_config(tab: Tab, json_config):
|
|
|
589
605
|
def build_config_message(tab: Tab, config_dict) -> dict:
|
|
590
606
|
"""Convert the client-supplied OrderedDict into a JSON-friendly message."""
|
|
591
607
|
plots = []
|
|
592
|
-
for key, plot_description in config_dict
|
|
593
|
-
if "non_plot_labels" in plot_description:
|
|
594
|
-
continue
|
|
595
|
-
if "controls" in plot_description:
|
|
596
|
-
continue
|
|
608
|
+
for key, plot_description in _expand_config(config_dict)[0]:
|
|
597
609
|
plots.append(
|
|
598
610
|
{
|
|
599
611
|
"key": key,
|
|
@@ -1529,8 +1541,10 @@ _SNAPSHOT_TEMPLATE = """<!doctype html>
|
|
|
1529
1541
|
<style>
|
|
1530
1542
|
html, body { margin: 0; padding: 0; background: #fafafa; color: #222;
|
|
1531
1543
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
|
|
1532
|
-
#plots { display:
|
|
1533
|
-
|
|
1544
|
+
#plots { display: flex; flex-direction: column; gap: 12px;
|
|
1545
|
+
max-width: 900px; margin: 0 auto; padding: 20px 16px; }
|
|
1546
|
+
.plot-row { display: grid; grid-template-columns: repeat(var(--plot-columns, 1), minmax(320px, 1fr));
|
|
1547
|
+
gap: 12px; min-width: 0; overflow-x: auto; }
|
|
1534
1548
|
.plot-wrap { background: #fff; border: 1px solid #ddd; border-radius: 4px; padding: 8px; min-width: 0; }
|
|
1535
1549
|
.snap-footer { text-align: center; font-size: 12px; color: #999; padding: 8px 0 20px; }
|
|
1536
1550
|
.snap-footer a { color: #2a5db0; text-decoration: none; }
|
|
@@ -1551,8 +1565,17 @@ _SNAPSHOT_TEMPLATE = """<!doctype html>
|
|
|
1551
1565
|
(function () {
|
|
1552
1566
|
const SNAP = __SNAPSHOT_JSON__;
|
|
1553
1567
|
const plotsDiv = document.getElementById('plots');
|
|
1554
|
-
|
|
1555
|
-
|
|
1568
|
+
const layout = SNAP.layout || SNAP.plots.map((_, i) => ({kind: 'plot', index: i}));
|
|
1569
|
+
const maxColumns = Math.max(1, ...layout.map(entry =>
|
|
1570
|
+
entry.kind === 'row' ? entry.columns : (entry.kind === 'plot' ? SNAP.columns || 1 : 1)));
|
|
1571
|
+
plotsDiv.style.maxWidth = `${900 * maxColumns}px`;
|
|
1572
|
+
function newRow(columns) {
|
|
1573
|
+
const row = document.createElement('div');
|
|
1574
|
+
row.className = 'plot-row';
|
|
1575
|
+
row.style.setProperty('--plot-columns', columns);
|
|
1576
|
+
plotsDiv.appendChild(row);
|
|
1577
|
+
return row;
|
|
1578
|
+
}
|
|
1556
1579
|
const COLOR_MAP = { r:'rgb(255,0,0)', g:'rgb(0,200,0)', b:'rgb(0,0,255)',
|
|
1557
1580
|
c:'rgb(0,200,200)', m:'rgb(200,0,200)',
|
|
1558
1581
|
y:'rgb(200,200,0)', k:'rgb(0,0,0)' };
|
|
@@ -1565,7 +1588,7 @@ _SNAPSHOT_TEMPLATE = """<!doctype html>
|
|
|
1565
1588
|
}
|
|
1566
1589
|
const plots = [];
|
|
1567
1590
|
let traceOffset = 0;
|
|
1568
|
-
|
|
1591
|
+
function buildPlot(pcfg, parent) {
|
|
1569
1592
|
const xrange = SNAP.num_samples;
|
|
1570
1593
|
const xs = new Float64Array(xrange);
|
|
1571
1594
|
for (let i = 0; i < xrange; i++) xs[i] = i;
|
|
@@ -1585,7 +1608,7 @@ _SNAPSHOT_TEMPLATE = """<!doctype html>
|
|
|
1585
1608
|
}
|
|
1586
1609
|
const wrap = document.createElement('div');
|
|
1587
1610
|
wrap.className = 'plot-wrap';
|
|
1588
|
-
|
|
1611
|
+
parent.appendChild(wrap);
|
|
1589
1612
|
const data = [xs];
|
|
1590
1613
|
for (let t = 0; t < traceCount; t++) {
|
|
1591
1614
|
const src = SNAP.trace_data[traceOffset + t];
|
|
@@ -1608,6 +1631,20 @@ _SNAPSHOT_TEMPLATE = """<!doctype html>
|
|
|
1608
1631
|
plots.push({ uplot: u, wrap: wrap, data: data, xs: xs, xrange: xrange,
|
|
1609
1632
|
traceCount: traceCount, startIdx: traceOffset });
|
|
1610
1633
|
traceOffset += traceCount;
|
|
1634
|
+
}
|
|
1635
|
+
let implicitRow = null;
|
|
1636
|
+
layout.forEach(entry => {
|
|
1637
|
+
if (entry.kind === 'plot') {
|
|
1638
|
+
if (!implicitRow) implicitRow = newRow(SNAP.columns || 1);
|
|
1639
|
+
buildPlot(SNAP.plots[entry.index], implicitRow);
|
|
1640
|
+
} else if (entry.kind === 'row') {
|
|
1641
|
+
implicitRow = null;
|
|
1642
|
+
const row = newRow(entry.columns);
|
|
1643
|
+
entry.plots.forEach(index => buildPlot(SNAP.plots[index], row));
|
|
1644
|
+
} else {
|
|
1645
|
+
// Snapshots omit controls, but retain the row break around them.
|
|
1646
|
+
implicitRow = null;
|
|
1647
|
+
}
|
|
1611
1648
|
});
|
|
1612
1649
|
renderMathInElement(plotsDiv, {
|
|
1613
1650
|
delimiters: [
|
|
@@ -1699,9 +1736,7 @@ def _build_snapshot_html(tab: Tab, animate: bool) -> str:
|
|
|
1699
1736
|
|
|
1700
1737
|
plots = []
|
|
1701
1738
|
cfg = tab.config_dict or OrderedDict()
|
|
1702
|
-
for key, plot_description in cfg
|
|
1703
|
-
if "non_plot_labels" in plot_description or "controls" in plot_description:
|
|
1704
|
-
continue
|
|
1739
|
+
for key, plot_description in _expand_config(cfg)[0]:
|
|
1705
1740
|
plots.append({
|
|
1706
1741
|
"key": key,
|
|
1707
1742
|
"names": plot_description.get("names", []),
|
|
@@ -1722,6 +1757,7 @@ def _build_snapshot_html(tab: Tab, animate: bool) -> str:
|
|
|
1722
1757
|
payload = {
|
|
1723
1758
|
"plots": plots,
|
|
1724
1759
|
"columns": PLOT_COLUMNS,
|
|
1760
|
+
"layout": tab.layout,
|
|
1725
1761
|
"num_samples": int(hi - lo),
|
|
1726
1762
|
"trace_data": trace_data,
|
|
1727
1763
|
"animate": bool(animate),
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
#ws-status { font-size: calc(12px * var(--ui-scale)); color: #666; margin-left: auto; }
|
|
63
63
|
|
|
64
64
|
/* -------- Plots + controls (existing) -------- */
|
|
65
|
-
#plots { display:
|
|
65
|
+
#plots { display: flex; flex-direction: column; padding: 12px; gap: 12px; }
|
|
66
|
+
.plot-row { display: grid; grid-template-columns: repeat(var(--plot-columns, 1), minmax(320px, 1fr)); gap: 12px; min-width: 0; overflow-x: auto; }
|
|
66
67
|
.plot-wrap { background: #fff; border: 1px solid #ddd; border-radius: 4px; padding: 8px; min-width: 0; }
|
|
67
|
-
#plots .ctrl-row, #plots #empty-state { grid-column: 1 / -1; }
|
|
68
68
|
.plot-title { font-size: 13px; font-weight: 600; margin: 0 0 4px 4px; color: #333; }
|
|
69
69
|
#empty-state { padding: 28px 32px; color: #888; font-size: calc(14px * var(--ui-scale)); font-style: italic; }
|
|
70
70
|
/* Persistent banner the server raises when a client-supplied plot
|
|
@@ -822,7 +822,7 @@
|
|
|
822
822
|
plotsDiv.appendChild(d);
|
|
823
823
|
}
|
|
824
824
|
|
|
825
|
-
function buildOnePlot(pcfg, rowLayout, traceOffset) {
|
|
825
|
+
function buildOnePlot(pcfg, rowLayout, traceOffset, parent) {
|
|
826
826
|
const xrange = pcfg.xrange || 200;
|
|
827
827
|
const xs = new Float64Array(xrange);
|
|
828
828
|
for (let i = 0; i < xrange; i++) xs[i] = i;
|
|
@@ -854,7 +854,7 @@
|
|
|
854
854
|
: baseHeight;
|
|
855
855
|
const wrap = document.createElement('div');
|
|
856
856
|
wrap.className = 'plot-wrap';
|
|
857
|
-
|
|
857
|
+
parent.appendChild(wrap);
|
|
858
858
|
|
|
859
859
|
const opts = {
|
|
860
860
|
width: Math.max(240, wrap.clientWidth - 16),
|
|
@@ -900,7 +900,13 @@
|
|
|
900
900
|
function buildPlots(cfg) {
|
|
901
901
|
destroyPlots();
|
|
902
902
|
const columns = Number(cfg.columns) || (cfg.row_layout === false ? 2 : 1);
|
|
903
|
-
|
|
903
|
+
function newRow(count) {
|
|
904
|
+
const row = document.createElement('div');
|
|
905
|
+
row.className = 'plot-row';
|
|
906
|
+
row.style.setProperty('--plot-columns', Math.max(1, Math.floor(count)));
|
|
907
|
+
plotsDiv.appendChild(row);
|
|
908
|
+
return row;
|
|
909
|
+
}
|
|
904
910
|
|
|
905
911
|
const controls = cfg.controls || [];
|
|
906
912
|
const layout = (cfg.layout && cfg.layout.length)
|
|
@@ -909,12 +915,21 @@
|
|
|
909
915
|
.concat(controls.map((_, idx) => ({ kind: 'controls', index: idx })));
|
|
910
916
|
|
|
911
917
|
let traceOffset = 0;
|
|
918
|
+
let implicitRow = null;
|
|
912
919
|
layout.forEach(entry => {
|
|
913
920
|
if (entry.kind === 'plot') {
|
|
914
921
|
const pcfg = cfg.plots[entry.index];
|
|
915
922
|
if (!pcfg) return;
|
|
916
|
-
|
|
923
|
+
if (!implicitRow) implicitRow = newRow(columns);
|
|
924
|
+
traceOffset += buildOnePlot(pcfg, columns === 1, traceOffset, implicitRow);
|
|
925
|
+
} else if (entry.kind === 'row') {
|
|
926
|
+
implicitRow = null;
|
|
927
|
+
const row = newRow(entry.columns);
|
|
928
|
+
entry.plots.forEach(index => {
|
|
929
|
+
traceOffset += buildOnePlot(cfg.plots[index], entry.columns === 1, traceOffset, row);
|
|
930
|
+
});
|
|
917
931
|
} else if (entry.kind === 'controls') {
|
|
932
|
+
implicitRow = null;
|
|
918
933
|
const row = controls[entry.index];
|
|
919
934
|
if (!row) return;
|
|
920
935
|
plotsDiv.appendChild(buildControlRow(row));
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.15 → better_rtplot-0.4.16}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|