better-rtplot 0.4.5__tar.gz → 0.4.7__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.5 → better_rtplot-0.4.7}/PKG-INFO +1 -1
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/pyproject.toml +1 -1
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/client.py +18 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/server_browser.py +72 -4
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/index.html +48 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/LICENSE +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/README.md +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/interactive_test.py +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/saved_plots/.gitignore +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/server.py +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/server_browser_gui.py +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/auto-render.min.js +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/katex.min.css +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/katex.min.js +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/uPlot.iife.min.js +0 -0
- {better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/uPlot.min.css +0 -0
|
@@ -25,6 +25,13 @@ control_socket.setsockopt(zmq.RCVHWM, 1000)
|
|
|
25
25
|
_control_slider_values = {}
|
|
26
26
|
_control_button_events = []
|
|
27
27
|
|
|
28
|
+
# Cached payload of the most recent initialize_plots() call. The server
|
|
29
|
+
# uses this to recover from its own crashes: when its tab loses the
|
|
30
|
+
# config, it pushes a "resend_config" control event over the return
|
|
31
|
+
# channel, and poll_controls() re-PUBs this dict so the plot reattaches
|
|
32
|
+
# without the user restarting their script. None until first call.
|
|
33
|
+
plot_desc_dict = None
|
|
34
|
+
|
|
28
35
|
#Global variable to keep track of last connected address
|
|
29
36
|
# default is fixed publisher mode, therefore you don't connect
|
|
30
37
|
# to an address
|
|
@@ -506,6 +513,13 @@ def poll_controls():
|
|
|
506
513
|
(cleared after this call).
|
|
507
514
|
|
|
508
515
|
Call this from your tight loop before computing the next sample.
|
|
516
|
+
|
|
517
|
+
Also transparently handles ``resend_config`` requests from the server:
|
|
518
|
+
if the server crashed and reconnected (or the user clicked the
|
|
519
|
+
Reconnect button on a tab), it asks us to re-publish the cached
|
|
520
|
+
initialize_plots() payload so the plot rewires without the user
|
|
521
|
+
restarting their script. The request is consumed silently — the
|
|
522
|
+
returned ControlState only ever contains slider/button events.
|
|
509
523
|
"""
|
|
510
524
|
global _control_button_events
|
|
511
525
|
while True:
|
|
@@ -520,6 +534,10 @@ def poll_controls():
|
|
|
520
534
|
_control_button_events.append(event.get("id"))
|
|
521
535
|
elif evtype == "slider":
|
|
522
536
|
_control_slider_values[event.get("id")] = float(event.get("value", 0.0))
|
|
537
|
+
elif evtype == "resend_config":
|
|
538
|
+
if plot_desc_dict is not None:
|
|
539
|
+
socket.send_string(SENDING_PLOT_UPDATE)
|
|
540
|
+
socket.send_json(plot_desc_dict)
|
|
523
541
|
|
|
524
542
|
buttons = _control_button_events
|
|
525
543
|
_control_button_events = []
|
|
@@ -306,6 +306,12 @@ class Tab:
|
|
|
306
306
|
data_rate_hz: float = 0.0
|
|
307
307
|
_last_rx_ts: float = 0.0
|
|
308
308
|
|
|
309
|
+
# When the most recent client-supplied config failed to parse we
|
|
310
|
+
# stash the reason + a unix timestamp so the browser can surface
|
|
311
|
+
# "your last config was rejected at HH:MM:SS" instead of just
|
|
312
|
+
# silently rendering nothing. Cleared on the next successful parse.
|
|
313
|
+
last_config_error: Optional[dict] = None
|
|
314
|
+
|
|
309
315
|
def ensure_buffer(self):
|
|
310
316
|
if self.buffer is None:
|
|
311
317
|
self.buffer = np.zeros((INITIAL_NUM_TRACES, MAX_LOCAL_STORAGE))
|
|
@@ -344,6 +350,7 @@ def tab_public(t: Tab) -> dict:
|
|
|
344
350
|
"endpoint": t.endpoint,
|
|
345
351
|
"status": t.status,
|
|
346
352
|
"error": t.error,
|
|
353
|
+
"last_config_error": t.last_config_error,
|
|
347
354
|
}
|
|
348
355
|
|
|
349
356
|
|
|
@@ -711,8 +718,30 @@ async def zmq_receiver(tab: Tab):
|
|
|
711
718
|
tab._last_rx_ts = now
|
|
712
719
|
|
|
713
720
|
if category == RECEIVED_PLOT_UPDATE:
|
|
714
|
-
|
|
715
|
-
|
|
721
|
+
# Decode + parse are wrapped so a malformed config doesn't kill
|
|
722
|
+
# the receiver task. We stash the failure on the tab so the
|
|
723
|
+
# browser can show "your last config was rejected at HH:MM:SS"
|
|
724
|
+
# instead of looking like nothing happened. Existing valid
|
|
725
|
+
# state (if any) is preserved on parse failure.
|
|
726
|
+
try:
|
|
727
|
+
cfg = await sock.recv_json(object_pairs_hook=OrderedDict)
|
|
728
|
+
except Exception as exc: # noqa: BLE001
|
|
729
|
+
msg = f"Could not decode config JSON: {type(exc).__name__}: {exc}"
|
|
730
|
+
print(f"[{tab.id}] {msg}")
|
|
731
|
+
tab.last_config_error = {"message": msg, "timestamp": time.time()}
|
|
732
|
+
await broadcast_tab(tab.id)
|
|
733
|
+
continue
|
|
734
|
+
|
|
735
|
+
try:
|
|
736
|
+
parse_config(tab, cfg)
|
|
737
|
+
except Exception as exc: # noqa: BLE001
|
|
738
|
+
msg = f"Configuration rejected: {type(exc).__name__}: {exc}"
|
|
739
|
+
print(f"[{tab.id}] {msg}")
|
|
740
|
+
tab.last_config_error = {"message": msg, "timestamp": time.time()}
|
|
741
|
+
await broadcast_tab(tab.id)
|
|
742
|
+
continue
|
|
743
|
+
|
|
744
|
+
tab.last_config_error = None # clear: this one was good
|
|
716
745
|
tab.config_dict = cfg
|
|
717
746
|
tab.config_message = build_config_message(tab, cfg)
|
|
718
747
|
tab.initialized = True
|
|
@@ -921,6 +950,10 @@ async def create_connect_tab(
|
|
|
921
950
|
tabs[t.id] = t
|
|
922
951
|
_open_tab_sockets(t)
|
|
923
952
|
t.receiver_task = asyncio.create_task(zmq_receiver(t))
|
|
953
|
+
# If the client at the other end is already running and only called
|
|
954
|
+
# initialize_plots() once at its startup, this nudges it to resend
|
|
955
|
+
# the cached config so the new tab populates without a script restart.
|
|
956
|
+
asyncio.create_task(_request_config_resend(t))
|
|
924
957
|
if persist:
|
|
925
958
|
save_persisted_tabs()
|
|
926
959
|
await broadcast_tab_list()
|
|
@@ -956,13 +989,47 @@ async def rename_tab(tab_id: str, new_name: str):
|
|
|
956
989
|
await broadcast_tab(tab_id)
|
|
957
990
|
|
|
958
991
|
|
|
992
|
+
async def _request_config_resend(tab: "Tab"):
|
|
993
|
+
"""Nudge a still-running client to re-send its initialize_plots() payload.
|
|
994
|
+
|
|
995
|
+
The common scenario: the server crashed (or was restarted) mid-session
|
|
996
|
+
and lost the plot config it received the first time the client called
|
|
997
|
+
``initialize_plots()``. The client is still happily streaming data,
|
|
998
|
+
but the new server has nothing to plot it on. This sends a
|
|
999
|
+
``{"type": "resend_config"}`` control event over the tab's PUSH
|
|
1000
|
+
socket; the client's poll_controls() loop intercepts it and re-sends
|
|
1001
|
+
the cached config so the plot picks back up without the user having
|
|
1002
|
+
to restart their script.
|
|
1003
|
+
|
|
1004
|
+
Retries on a brief schedule because the client's PULL socket may need
|
|
1005
|
+
a moment to auto-reconnect to the freshly bound PUSH socket — a
|
|
1006
|
+
one-shot DONTWAIT send to a peer-less PUSH gets silently dropped.
|
|
1007
|
+
Stops early once we see ``tab.initialized`` flip to True (the client
|
|
1008
|
+
has answered) so we don't spam.
|
|
1009
|
+
"""
|
|
1010
|
+
for i, delay in enumerate((0.4, 1.0, 2.0)):
|
|
1011
|
+
await asyncio.sleep(delay)
|
|
1012
|
+
if tab.ctrl_sock is None:
|
|
1013
|
+
return
|
|
1014
|
+
if i > 0 and tab.initialized:
|
|
1015
|
+
return
|
|
1016
|
+
try:
|
|
1017
|
+
await tab.ctrl_sock.send_json(
|
|
1018
|
+
{"type": "resend_config"}, flags=zmq.DONTWAIT
|
|
1019
|
+
)
|
|
1020
|
+
except (zmq.Again, zmq.ZMQError):
|
|
1021
|
+
pass
|
|
1022
|
+
|
|
1023
|
+
|
|
959
1024
|
async def reconnect_tab(tab_id: str):
|
|
960
1025
|
"""Close and reopen ``tab_id``'s sockets, restart its receiver task.
|
|
961
1026
|
|
|
962
1027
|
The tab's buffer and plot config are preserved — if the sender on
|
|
963
1028
|
the other side is still running the same stream we avoid a visible
|
|
964
|
-
flash to empty.
|
|
965
|
-
|
|
1029
|
+
flash to empty. After the sockets come back up we ask the (likely
|
|
1030
|
+
still-running) client to re-send its plot config so the connection
|
|
1031
|
+
recovers cleanly even if the server itself restarted in the
|
|
1032
|
+
meantime.
|
|
966
1033
|
"""
|
|
967
1034
|
t = tabs.get(tab_id)
|
|
968
1035
|
if t is None:
|
|
@@ -976,6 +1043,7 @@ async def reconnect_tab(tab_id: str):
|
|
|
976
1043
|
t.data_rate_hz = 0.0
|
|
977
1044
|
t._last_rx_ts = 0.0
|
|
978
1045
|
t.receiver_task = asyncio.create_task(zmq_receiver(t))
|
|
1046
|
+
asyncio.create_task(_request_config_resend(t))
|
|
979
1047
|
await broadcast_tab(tab_id)
|
|
980
1048
|
|
|
981
1049
|
|
|
@@ -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.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_SansSerif-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2
RENAMED
|
File without changes
|
{better_rtplot-0.4.5 → better_rtplot-0.4.7}/rtplot/static/katex/fonts/KaTeX_Typewriter-Regular.woff2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|