better-rtplot 0.4.6__tar.gz → 0.4.8__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.6 → better_rtplot-0.4.8}/PKG-INFO +1 -1
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/pyproject.toml +1 -1
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/client.py +6 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/server_browser.py +123 -44
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/index.html +48 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/LICENSE +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/README.md +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/interactive_test.py +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/saved_plots/.gitignore +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/server.py +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/server_browser_gui.py +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/auto-render.min.js +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/katex.min.css +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/katex.min.js +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/uPlot.iife.min.js +0 -0
- {better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/uPlot.min.css +0 -0
|
@@ -538,6 +538,12 @@ def poll_controls():
|
|
|
538
538
|
if plot_desc_dict is not None:
|
|
539
539
|
socket.send_string(SENDING_PLOT_UPDATE)
|
|
540
540
|
socket.send_json(plot_desc_dict)
|
|
541
|
+
# Surface a one-line confirmation so the user can see in
|
|
542
|
+
# their script log that the recovery handshake worked.
|
|
543
|
+
# If they never see this after clicking Reconnect, the
|
|
544
|
+
# PUSH/PULL channel back from the server isn't wired —
|
|
545
|
+
# check that poll_controls() is actually being called.
|
|
546
|
+
print("rtplot.client: resent cached initialize_plots() config")
|
|
541
547
|
|
|
542
548
|
buttons = _control_button_events
|
|
543
549
|
_control_button_events = []
|
|
@@ -215,7 +215,13 @@ RECEIVED_DISPLAY = 4
|
|
|
215
215
|
BIND_ME_ID = "bind_me"
|
|
216
216
|
BIND_ME_NAME = "Shared - Bind to me"
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
# Path to the persisted tabs JSON. Honors RTPLOT_TABS_FILE so tests
|
|
219
|
+
# (and operators with multi-user setups) can override without symlinking
|
|
220
|
+
# their HOME directory or losing access to ~/.local site-packages.
|
|
221
|
+
TABS_FILE = os.environ.get(
|
|
222
|
+
"RTPLOT_TABS_FILE",
|
|
223
|
+
os.path.join(os.path.expanduser("~"), ".rtplot", "tabs.json"),
|
|
224
|
+
)
|
|
219
225
|
|
|
220
226
|
zmq_ctx = zmq.asyncio.Context()
|
|
221
227
|
|
|
@@ -306,6 +312,12 @@ class Tab:
|
|
|
306
312
|
data_rate_hz: float = 0.0
|
|
307
313
|
_last_rx_ts: float = 0.0
|
|
308
314
|
|
|
315
|
+
# When the most recent client-supplied config failed to parse we
|
|
316
|
+
# stash the reason + a unix timestamp so the browser can surface
|
|
317
|
+
# "your last config was rejected at HH:MM:SS" instead of just
|
|
318
|
+
# silently rendering nothing. Cleared on the next successful parse.
|
|
319
|
+
last_config_error: Optional[dict] = None
|
|
320
|
+
|
|
309
321
|
def ensure_buffer(self):
|
|
310
322
|
if self.buffer is None:
|
|
311
323
|
self.buffer = np.zeros((INITIAL_NUM_TRACES, MAX_LOCAL_STORAGE))
|
|
@@ -344,6 +356,7 @@ def tab_public(t: Tab) -> dict:
|
|
|
344
356
|
"endpoint": t.endpoint,
|
|
345
357
|
"status": t.status,
|
|
346
358
|
"error": t.error,
|
|
359
|
+
"last_config_error": t.last_config_error,
|
|
347
360
|
}
|
|
348
361
|
|
|
349
362
|
|
|
@@ -595,43 +608,67 @@ def _remove_ws(ws):
|
|
|
595
608
|
def _open_tab_sockets(tab: Tab):
|
|
596
609
|
"""Open ``tab``'s data + control sockets according to its mode.
|
|
597
610
|
|
|
598
|
-
|
|
599
|
-
|
|
611
|
+
For bind tabs, the just-closed sockets may leave the OS-level port
|
|
612
|
+
in a transient state for a few hundred ms (especially on Windows /
|
|
613
|
+
WSL). We retry the bind with a small backoff so the user-visible
|
|
614
|
+
Reconnect button doesn't fail spuriously the very first time. On
|
|
615
|
+
sustained failure we set tab.status="error" + tab.error=<reason>;
|
|
616
|
+
the tab stays in the list but won't stream until re-opened.
|
|
600
617
|
"""
|
|
601
618
|
# Close any prior sockets first (e.g. on retry).
|
|
602
619
|
_close_tab_sockets(tab)
|
|
603
620
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
ctrl.
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
621
|
+
last_exc: Exception | None = None
|
|
622
|
+
attempts = 4 if tab.mode == "bind" else 1
|
|
623
|
+
for attempt in range(attempts):
|
|
624
|
+
try:
|
|
625
|
+
data = zmq_ctx.socket(zmq.SUB)
|
|
626
|
+
data.setsockopt_string(zmq.SUBSCRIBE, "")
|
|
627
|
+
data.setsockopt(zmq.LINGER, 0)
|
|
628
|
+
ctrl = zmq_ctx.socket(zmq.PUSH)
|
|
629
|
+
ctrl.setsockopt(zmq.SNDHWM, 1000)
|
|
630
|
+
ctrl.setsockopt(zmq.LINGER, 0)
|
|
631
|
+
|
|
632
|
+
if tab.mode == "bind":
|
|
633
|
+
data.bind(f"tcp://*:{ZMQ_DEFAULT_PORT}")
|
|
634
|
+
ctrl.bind(f"tcp://*:{ZMQ_CONTROL_PORT}")
|
|
635
|
+
tab.endpoint = f"*:{ZMQ_DEFAULT_PORT}"
|
|
636
|
+
print(f"[{tab.id}] ZMQ: bound on tcp://*:{ZMQ_DEFAULT_PORT}")
|
|
637
|
+
else:
|
|
638
|
+
data_ep, label = _normalize_connect_target(tab.endpoint)
|
|
639
|
+
data.connect(data_ep)
|
|
640
|
+
ctrl_ep, _ = _normalize_connect_target(
|
|
641
|
+
_control_target_from_data(tab.endpoint), port=ZMQ_CONTROL_PORT
|
|
642
|
+
)
|
|
643
|
+
ctrl.connect(ctrl_ep)
|
|
644
|
+
tab.endpoint = label
|
|
645
|
+
print(f"[{tab.id}] ZMQ: connected to {data_ep} (ctrl {ctrl_ep})")
|
|
625
646
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
647
|
+
tab.data_sock = data
|
|
648
|
+
tab.ctrl_sock = ctrl
|
|
649
|
+
tab.status = "idle"
|
|
650
|
+
tab.error = None
|
|
651
|
+
return
|
|
652
|
+
except Exception as exc: # noqa: BLE001
|
|
653
|
+
last_exc = exc
|
|
654
|
+
# Drop the half-built sockets before retrying so we don't
|
|
655
|
+
# leak descriptors and so the next attempt re-takes the port.
|
|
656
|
+
try: data.close(0)
|
|
657
|
+
except Exception: pass
|
|
658
|
+
try: ctrl.close(0)
|
|
659
|
+
except Exception: pass
|
|
660
|
+
if attempt < attempts - 1:
|
|
661
|
+
wait = 0.15 * (attempt + 1)
|
|
662
|
+
print(
|
|
663
|
+
f"[{tab.id}] ZMQ open attempt {attempt + 1}/{attempts}"
|
|
664
|
+
f" failed ({exc}); retrying in {wait:.2f}s"
|
|
665
|
+
)
|
|
666
|
+
time.sleep(wait)
|
|
667
|
+
|
|
668
|
+
tab.status = "error"
|
|
669
|
+
tab.error = str(last_exc) if last_exc is not None else "unknown error"
|
|
670
|
+
print(f"[{tab.id}] ZMQ open failed after {attempts} attempts: {last_exc}")
|
|
671
|
+
_close_tab_sockets(tab)
|
|
635
672
|
|
|
636
673
|
|
|
637
674
|
def _close_tab_sockets(tab: Tab):
|
|
@@ -711,8 +748,30 @@ async def zmq_receiver(tab: Tab):
|
|
|
711
748
|
tab._last_rx_ts = now
|
|
712
749
|
|
|
713
750
|
if category == RECEIVED_PLOT_UPDATE:
|
|
714
|
-
|
|
715
|
-
|
|
751
|
+
# Decode + parse are wrapped so a malformed config doesn't kill
|
|
752
|
+
# the receiver task. We stash the failure on the tab so the
|
|
753
|
+
# browser can show "your last config was rejected at HH:MM:SS"
|
|
754
|
+
# instead of looking like nothing happened. Existing valid
|
|
755
|
+
# state (if any) is preserved on parse failure.
|
|
756
|
+
try:
|
|
757
|
+
cfg = await sock.recv_json(object_pairs_hook=OrderedDict)
|
|
758
|
+
except Exception as exc: # noqa: BLE001
|
|
759
|
+
msg = f"Could not decode config JSON: {type(exc).__name__}: {exc}"
|
|
760
|
+
print(f"[{tab.id}] {msg}")
|
|
761
|
+
tab.last_config_error = {"message": msg, "timestamp": time.time()}
|
|
762
|
+
await broadcast_tab(tab.id)
|
|
763
|
+
continue
|
|
764
|
+
|
|
765
|
+
try:
|
|
766
|
+
parse_config(tab, cfg)
|
|
767
|
+
except Exception as exc: # noqa: BLE001
|
|
768
|
+
msg = f"Configuration rejected: {type(exc).__name__}: {exc}"
|
|
769
|
+
print(f"[{tab.id}] {msg}")
|
|
770
|
+
tab.last_config_error = {"message": msg, "timestamp": time.time()}
|
|
771
|
+
await broadcast_tab(tab.id)
|
|
772
|
+
continue
|
|
773
|
+
|
|
774
|
+
tab.last_config_error = None # clear: this one was good
|
|
716
775
|
tab.config_dict = cfg
|
|
717
776
|
tab.config_message = build_config_message(tab, cfg)
|
|
718
777
|
tab.initialized = True
|
|
@@ -972,24 +1031,44 @@ async def _request_config_resend(tab: "Tab"):
|
|
|
972
1031
|
the cached config so the plot picks back up without the user having
|
|
973
1032
|
to restart their script.
|
|
974
1033
|
|
|
975
|
-
Retries
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1034
|
+
Retries over a ~10 s window because:
|
|
1035
|
+
* the client's PULL socket may need a moment to auto-reconnect to
|
|
1036
|
+
the freshly bound PUSH socket — especially over a WAN/SSH link;
|
|
1037
|
+
* a slow user loop may only call poll_controls() once per second,
|
|
1038
|
+
so the message can sit unread in the PULL queue for a while;
|
|
1039
|
+
* the client may not be running at all (no peer), in which case
|
|
1040
|
+
the queued message is just dropped when the next reconnect happens.
|
|
1041
|
+
Stops early once tab.initialized flips True (the client answered).
|
|
980
1042
|
"""
|
|
981
|
-
|
|
1043
|
+
schedule = (0.3, 0.7, 1.5, 2.5, 5.0) # cumulative ~10 s
|
|
1044
|
+
started_initialized = tab.initialized
|
|
1045
|
+
for i, delay in enumerate(schedule):
|
|
982
1046
|
await asyncio.sleep(delay)
|
|
983
1047
|
if tab.ctrl_sock is None:
|
|
984
1048
|
return
|
|
985
|
-
if i > 0 and tab.initialized:
|
|
1049
|
+
if i > 0 and tab.initialized and not started_initialized:
|
|
986
1050
|
return
|
|
987
1051
|
try:
|
|
988
1052
|
await tab.ctrl_sock.send_json(
|
|
989
1053
|
{"type": "resend_config"}, flags=zmq.DONTWAIT
|
|
990
1054
|
)
|
|
991
|
-
|
|
992
|
-
|
|
1055
|
+
if DEBUG_TEXT_ENABLED:
|
|
1056
|
+
print(f"[{tab.id}] resend_config sent (attempt {i + 1}/{len(schedule)})")
|
|
1057
|
+
except (zmq.Again, zmq.ZMQError) as exc:
|
|
1058
|
+
if DEBUG_TEXT_ENABLED:
|
|
1059
|
+
print(
|
|
1060
|
+
f"[{tab.id}] resend_config attempt {i + 1}/{len(schedule)}"
|
|
1061
|
+
f" failed: {exc} (likely no peer connected yet)"
|
|
1062
|
+
)
|
|
1063
|
+
# If we asked the client to re-init but nothing came back, surface
|
|
1064
|
+
# a hint so the user knows where to look. Common causes: the client
|
|
1065
|
+
# is on rtplot < 0.4.6 (no resend_config handler), or its loop never
|
|
1066
|
+
# calls poll_controls(), or there's no client running at all.
|
|
1067
|
+
if not started_initialized and not tab.initialized:
|
|
1068
|
+
print(
|
|
1069
|
+
f"[{tab.id}] no config received after resend_config nudges."
|
|
1070
|
+
" Is the client running rtplot >= 0.4.6 and calling poll_controls()?"
|
|
1071
|
+
)
|
|
993
1072
|
|
|
994
1073
|
|
|
995
1074
|
async def reconnect_tab(tab_id: str):
|
|
@@ -67,6 +67,13 @@
|
|
|
67
67
|
.plot-wrap { background: #fff; border: 1px solid #ddd; border-radius: 4px; padding: 8px; flex: 1 1 auto; min-width: 320px; }
|
|
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
|
+
/* Persistent banner the server raises when a client-supplied plot
|
|
71
|
+
config fails to parse — the user used to be left guessing why
|
|
72
|
+
nothing rendered; now they get the reason + a local timestamp. */
|
|
73
|
+
.config-error-banner { margin: 12px 12px 0 12px; padding: 10px 14px; background: #fce8e8; color: #8a1a1a; border: 1px solid #f0c2c2; border-left: 4px solid #d24a4a; border-radius: 4px; font-size: calc(13px * var(--ui-scale)); }
|
|
74
|
+
.config-error-banner .ce-title { font-weight: 600; margin-right: 8px; }
|
|
75
|
+
.config-error-banner .ce-when { color: #6b1818; opacity: 0.8; font-family: monospace; font-size: calc(12px * var(--ui-scale)); }
|
|
76
|
+
.config-error-banner .ce-msg { display: block; margin-top: 4px; font-family: monospace; font-size: calc(12px * var(--ui-scale)); white-space: pre-wrap; word-break: break-word; }
|
|
70
77
|
:root { --ctrl-unit-h: 38px; --ui-scale: 1; }
|
|
71
78
|
.ctrl-row { display: flex; gap: 12px; align-items: center; padding: 10px 14px; background: #fff; border: 1px solid #ddd; border-radius: 4px; flex-wrap: wrap; }
|
|
72
79
|
.ctrl-item { display: flex; align-items: center; gap: 6px; }
|
|
@@ -187,6 +194,7 @@
|
|
|
187
194
|
<div id="res-unavail" class="res-unavail" style="display:none;">psutil not installed — pip install psutil for live stats</div>
|
|
188
195
|
</div>
|
|
189
196
|
</div>
|
|
197
|
+
<div id="config-error-banner" class="config-error-banner" style="display:none"></div>
|
|
190
198
|
<div id="plots" class="row"></div>
|
|
191
199
|
<script src="/static/uPlot.iife.min.js"></script>
|
|
192
200
|
<script src="/static/katex/katex.min.js"></script>
|
|
@@ -210,6 +218,43 @@
|
|
|
210
218
|
return 'rgb(0,0,0)';
|
|
211
219
|
}
|
|
212
220
|
|
|
221
|
+
const configErrorBanner = document.getElementById('config-error-banner');
|
|
222
|
+
|
|
223
|
+
function fmtLocalTimestamp(unixSeconds) {
|
|
224
|
+
const d = new Date(unixSeconds * 1000);
|
|
225
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
226
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +
|
|
227
|
+
`${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function applyConfigErrorBanner() {
|
|
231
|
+
const t = activeTab && knownTabs[activeTab];
|
|
232
|
+
const err = t && t.last_config_error;
|
|
233
|
+
if (!err) {
|
|
234
|
+
configErrorBanner.style.display = 'none';
|
|
235
|
+
configErrorBanner.innerHTML = '';
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
// Build with textContent on the leaf nodes so user-controlled
|
|
239
|
+
// strings (the error message) can't inject HTML.
|
|
240
|
+
configErrorBanner.innerHTML = '';
|
|
241
|
+
const head = document.createElement('div');
|
|
242
|
+
const title = document.createElement('span');
|
|
243
|
+
title.className = 'ce-title';
|
|
244
|
+
title.textContent = 'Last configuration was rejected';
|
|
245
|
+
head.appendChild(title);
|
|
246
|
+
const when = document.createElement('span');
|
|
247
|
+
when.className = 'ce-when';
|
|
248
|
+
when.textContent = `at ${fmtLocalTimestamp(err.timestamp)}`;
|
|
249
|
+
head.appendChild(when);
|
|
250
|
+
configErrorBanner.appendChild(head);
|
|
251
|
+
const body = document.createElement('span');
|
|
252
|
+
body.className = 'ce-msg';
|
|
253
|
+
body.textContent = err.message || '';
|
|
254
|
+
configErrorBanner.appendChild(body);
|
|
255
|
+
configErrorBanner.style.display = '';
|
|
256
|
+
}
|
|
257
|
+
|
|
213
258
|
// ---- LaTeX rendering ($...$ inline, $$...$$ display) ----
|
|
214
259
|
// KaTeX's auto-render walks an element's text nodes looking for the
|
|
215
260
|
// configured delimiters, so plain labels without `$` are completely
|
|
@@ -1111,6 +1156,7 @@
|
|
|
1111
1156
|
showEmptyState('Loading tab \u2026');
|
|
1112
1157
|
const t = knownTabs[id];
|
|
1113
1158
|
setZmqMode(t.mode, t.endpoint);
|
|
1159
|
+
applyConfigErrorBanner();
|
|
1114
1160
|
sendCtrl({ type: 'tab_subscribe', id: id });
|
|
1115
1161
|
}
|
|
1116
1162
|
|
|
@@ -1183,6 +1229,7 @@
|
|
|
1183
1229
|
if (activeTab) {
|
|
1184
1230
|
const t = knownTabs[activeTab];
|
|
1185
1231
|
setZmqMode(t.mode, t.endpoint);
|
|
1232
|
+
applyConfigErrorBanner();
|
|
1186
1233
|
sendCtrl({ type: 'tab_subscribe', id: activeTab });
|
|
1187
1234
|
showEmptyState('Loading tab \u2026');
|
|
1188
1235
|
}
|
|
@@ -1193,6 +1240,7 @@
|
|
|
1193
1240
|
renderTabs();
|
|
1194
1241
|
if (msg.tab.id === activeTab) {
|
|
1195
1242
|
setZmqMode(msg.tab.mode, msg.tab.endpoint);
|
|
1243
|
+
applyConfigErrorBanner();
|
|
1196
1244
|
}
|
|
1197
1245
|
}
|
|
1198
1246
|
} else if (msg.type === 'tab_removed') {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_SansSerif-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.6 → better_rtplot-0.4.8}/rtplot/static/katex/fonts/KaTeX_Typewriter-Regular.woff2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|