runtimepy 5.14.2__py3-none-any.whl → 5.15.0__py3-none-any.whl
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.
- runtimepy/__init__.py +2 -2
- runtimepy/channel/__init__.py +1 -4
- runtimepy/channel/environment/__init__.py +93 -2
- runtimepy/channel/environment/create.py +16 -1
- runtimepy/channel/environment/sample.py +10 -2
- runtimepy/channel/registry.py +2 -3
- runtimepy/codec/protocol/base.py +34 -14
- runtimepy/codec/protocol/json.py +5 -3
- runtimepy/codec/system/__init__.py +6 -2
- runtimepy/control/source.py +1 -1
- runtimepy/data/404.md +16 -0
- runtimepy/data/base.yaml +3 -0
- runtimepy/data/css/bootstrap_extra.css +59 -44
- runtimepy/data/css/main.css +23 -4
- runtimepy/data/dummy_load.yaml +2 -2
- runtimepy/data/factories.yaml +1 -0
- runtimepy/data/js/classes/App.js +54 -2
- runtimepy/data/js/classes/ChannelTable.js +6 -8
- runtimepy/data/js/classes/Plot.js +9 -4
- runtimepy/data/js/classes/TabFilter.js +47 -9
- runtimepy/data/js/classes/TabInterface.js +106 -11
- runtimepy/data/js/classes/WindowHashManager.js +30 -15
- runtimepy/data/js/init.js +18 -1
- runtimepy/data/js/markdown_page.js +10 -0
- runtimepy/data/schemas/BitFields.yaml +9 -0
- runtimepy/data/schemas/RuntimeEnum.yaml +6 -0
- runtimepy/data/schemas/StructConfig.yaml +9 -1
- runtimepy/data/static/css/bootstrap-icons.min.css +4 -3
- runtimepy/data/static/css/bootstrap.min.css +3 -4
- runtimepy/data/static/css/fonts/bootstrap-icons.woff +0 -0
- runtimepy/data/static/css/fonts/bootstrap-icons.woff2 +0 -0
- runtimepy/data/static/js/bootstrap.bundle.min.js +5 -4
- runtimepy/data/static/js/webglplot.umd.min.js +2 -1
- runtimepy/data/static/svg/outline-dark.svg +22 -0
- runtimepy/data/static/svg/outline-light.svg +22 -0
- runtimepy/enum/__init__.py +13 -1
- runtimepy/enum/registry.py +13 -1
- runtimepy/message/__init__.py +3 -3
- runtimepy/mixins/logging.py +6 -1
- runtimepy/net/__init__.py +0 -2
- runtimepy/net/arbiter/info.py +36 -4
- runtimepy/net/arbiter/struct/__init__.py +3 -2
- runtimepy/net/connection.py +4 -5
- runtimepy/net/html/__init__.py +29 -11
- runtimepy/net/html/bootstrap/__init__.py +2 -2
- runtimepy/net/html/bootstrap/elements.py +44 -24
- runtimepy/net/html/bootstrap/tabs.py +18 -11
- runtimepy/net/http/__init__.py +3 -3
- runtimepy/net/http/request_target.py +3 -3
- runtimepy/net/mixin.py +4 -2
- runtimepy/net/server/__init__.py +16 -9
- runtimepy/net/server/app/__init__.py +1 -0
- runtimepy/net/server/app/create.py +3 -3
- runtimepy/net/server/app/env/__init__.py +28 -4
- runtimepy/net/server/app/env/settings.py +4 -7
- runtimepy/net/server/app/env/tab/controls.py +141 -27
- runtimepy/net/server/app/env/tab/html.py +68 -26
- runtimepy/net/server/app/env/widgets.py +115 -61
- runtimepy/net/server/app/landing_page.py +1 -1
- runtimepy/net/server/html.py +2 -2
- runtimepy/net/server/json.py +1 -1
- runtimepy/net/server/markdown.py +18 -12
- runtimepy/net/server/mux.py +29 -0
- runtimepy/net/stream/__init__.py +6 -5
- runtimepy/net/stream/base.py +4 -2
- runtimepy/net/tcp/connection.py +5 -3
- runtimepy/net/tcp/http/__init__.py +10 -9
- runtimepy/net/tcp/protocol.py +2 -2
- runtimepy/net/tcp/scpi/__init__.py +5 -2
- runtimepy/net/tcp/telnet/__init__.py +2 -1
- runtimepy/net/udp/connection.py +10 -6
- runtimepy/net/udp/protocol.py +5 -6
- runtimepy/net/udp/queue.py +5 -2
- runtimepy/net/udp/tftp/base.py +2 -1
- runtimepy/net/websocket/connection.py +50 -8
- runtimepy/primitives/array/__init__.py +7 -5
- runtimepy/primitives/base.py +3 -2
- runtimepy/primitives/field/__init__.py +35 -2
- runtimepy/primitives/field/fields.py +11 -2
- runtimepy/primitives/field/manager/base.py +19 -2
- runtimepy/primitives/serializable/base.py +5 -2
- runtimepy/primitives/serializable/fixed.py +5 -2
- runtimepy/primitives/serializable/prefixed.py +4 -1
- runtimepy/primitives/types/base.py +4 -1
- runtimepy/primitives/types/bounds.py +10 -4
- runtimepy/registry/__init__.py +20 -0
- runtimepy/registry/name.py +6 -0
- runtimepy/requirements.txt +2 -2
- runtimepy/ui/controls.py +20 -1
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/METADATA +6 -6
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/RECORD +95 -92
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/WHEEL +1 -1
- runtimepy/data/404.html +0 -7
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/entry_points.txt +0 -0
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/licenses/LICENSE +0 -0
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/top_level.txt +0 -0
runtimepy/data/css/main.css
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
html {
|
|
2
2
|
height: 100%;
|
|
3
|
+
max-height: 100%;
|
|
3
4
|
width: 100%;
|
|
5
|
+
max-width: 100%;
|
|
4
6
|
position: fixed;
|
|
5
7
|
}
|
|
6
8
|
|
|
@@ -15,10 +17,6 @@ body > :first-child {
|
|
|
15
17
|
height: 100%;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
#runtimepy {
|
|
19
|
-
height: 100%;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
20
|
#runtimepy-tabs {
|
|
23
21
|
width: min-content;
|
|
24
22
|
}
|
|
@@ -46,12 +44,33 @@ body > :first-child {
|
|
|
46
44
|
|
|
47
45
|
.toggle-value {
|
|
48
46
|
/* placeholder */
|
|
47
|
+
background-color: var(--bs-body-bg);
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
.window-button {
|
|
52
51
|
border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-link-hover-color) !important;
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
.logo-outline-background {
|
|
55
|
+
background-position: center;
|
|
56
|
+
background-repeat: no-repeat;
|
|
57
|
+
background-size: auto 66.67%;
|
|
58
|
+
}
|
|
59
|
+
[data-bs-theme=dark] .logo-outline-background {
|
|
60
|
+
background-image: url(/static/svg/outline-dark.svg);
|
|
61
|
+
}
|
|
62
|
+
[data-bs-theme=light] .logo-outline-background {
|
|
63
|
+
background-image: url(/static/svg/outline-light.svg);
|
|
64
|
+
}
|
|
65
|
+
|
|
55
66
|
:focus {
|
|
56
67
|
outline: none;
|
|
57
68
|
}
|
|
69
|
+
|
|
70
|
+
.overscroll-behavior-x-none {
|
|
71
|
+
overscroll-behavior-x: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.overscroll-behavior-none {
|
|
75
|
+
overscroll-behavior: none;
|
|
76
|
+
}
|
runtimepy/data/dummy_load.yaml
CHANGED
|
@@ -75,7 +75,7 @@ structs:
|
|
|
75
75
|
|
|
76
76
|
Should be shown for peer process as well?
|
|
77
77
|
|
|
78
|
-
- name: struct2
|
|
78
|
+
- name: example.struct2
|
|
79
79
|
factory: sample_struct
|
|
80
80
|
config:
|
|
81
81
|
markdown: |
|
|
@@ -83,7 +83,7 @@ structs:
|
|
|
83
83
|
|
|
84
84
|
One of the structs of all time.
|
|
85
85
|
|
|
86
|
-
- name: struct3
|
|
86
|
+
- name: example.struct3
|
|
87
87
|
factory: sample_struct
|
|
88
88
|
|
|
89
89
|
# Sample peer processes.
|
runtimepy/data/factories.yaml
CHANGED
|
@@ -37,6 +37,7 @@ factories:
|
|
|
37
37
|
- {name: runtimepy.control.step.StepperToggler}
|
|
38
38
|
|
|
39
39
|
# Useful structs.
|
|
40
|
+
- {name: runtimepy.net.arbiter.info.RuntimeStruct}
|
|
40
41
|
- {name: runtimepy.net.arbiter.info.TrigStruct}
|
|
41
42
|
- {name: runtimepy.net.arbiter.info.SampleStruct}
|
|
42
43
|
- {name: runtimepy.net.server.struct.UiState}
|
runtimepy/data/js/classes/App.js
CHANGED
|
@@ -20,7 +20,7 @@ class App {
|
|
|
20
20
|
worker.addEventListener("message", async (event) => {
|
|
21
21
|
if (event.data == 0) {
|
|
22
22
|
/* Manage settings modal. */
|
|
23
|
-
let _modal = document.getElementById("runtimepy-
|
|
23
|
+
let _modal = document.getElementById("runtimepy-settings");
|
|
24
24
|
if (_modal) {
|
|
25
25
|
modalManager = new PlotModalManager(_modal);
|
|
26
26
|
}
|
|
@@ -53,12 +53,64 @@ class App {
|
|
|
53
53
|
this.switchTab(hash.tab);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
hash.updateTabFilter(hash.tabFilter);
|
|
57
|
+
|
|
56
58
|
/* Handle settings controls. */
|
|
57
59
|
loadSettings();
|
|
58
60
|
|
|
59
61
|
/* Handle individual settings. */
|
|
60
62
|
this.handleInitialMinTxPeriod();
|
|
61
63
|
|
|
64
|
+
/* Handle channel-table expand button. */
|
|
65
|
+
let _button = document.getElementById("open-channels-button");
|
|
66
|
+
if (_button) {
|
|
67
|
+
_button.onclick = () => {
|
|
68
|
+
/* Ensure channel table is visible. */
|
|
69
|
+
if (!hash.channelsShown && hash.channelsButton) {
|
|
70
|
+
hash.channelsButton.click();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Ensure channel table is at maximum width. */
|
|
74
|
+
if (shown_tab in tabs) {
|
|
75
|
+
let elem = tabs[shown_tab].query(".channel-column");
|
|
76
|
+
if (elem) {
|
|
77
|
+
elem.style.width = window.innerWidth + "px";
|
|
78
|
+
tabs[shown_tab].correctVerticalBarPosition();
|
|
79
|
+
tabs[shown_tab].focus();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
_button = document.getElementById("dedent-channels-button");
|
|
85
|
+
if (_button) {
|
|
86
|
+
_button.onclick = () => {
|
|
87
|
+
if (!hash.channelsShown) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Reduce channel table width. */
|
|
92
|
+
if (shown_tab in tabs) {
|
|
93
|
+
let elem = tabs[shown_tab].query(".channel-column");
|
|
94
|
+
if (elem) {
|
|
95
|
+
let newWidth = elem.getBoundingClientRect().width - 50;
|
|
96
|
+
if (newWidth > 0) {
|
|
97
|
+
elem.style.width = newWidth + "px";
|
|
98
|
+
tabs[shown_tab].focus();
|
|
99
|
+
} else {
|
|
100
|
+
hash.channelsButton.click();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Set initial focus. */
|
|
108
|
+
if (hash.tabsShown && tabFilter) {
|
|
109
|
+
tabFilter.input.focus();
|
|
110
|
+
} else if (hash.channelsShown && shown_tab in tabs) {
|
|
111
|
+
tabs[shown_tab].focus();
|
|
112
|
+
}
|
|
113
|
+
|
|
62
114
|
startMainLoop();
|
|
63
115
|
}
|
|
64
116
|
}, {once : true});
|
|
@@ -110,7 +162,7 @@ function startMainLoop() {
|
|
|
110
162
|
if (splash) {
|
|
111
163
|
let curr = window.getComputedStyle(splash).getPropertyValue("opacity");
|
|
112
164
|
if (curr > 0) {
|
|
113
|
-
splash.style.opacity = curr - Math.min(0.05, deltaT /
|
|
165
|
+
splash.style.opacity = curr - Math.min(0.05, deltaT / 1000);
|
|
114
166
|
} else {
|
|
115
167
|
splash.style.display = "none";
|
|
116
168
|
splash = undefined;
|
|
@@ -20,14 +20,12 @@ class ChannelTable {
|
|
|
20
20
|
for (let input of table.querySelectorAll("input.channel-value-input")) {
|
|
21
21
|
this.channelInputs[input.id] = input;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
};
|
|
23
|
+
/* Register handler for pressing enter. */
|
|
24
|
+
input.onkeypress = (event) => {
|
|
25
|
+
if (event.key == "Enter") {
|
|
26
|
+
this.worker.command(`set ${input.id} ${input.value}`);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
31
29
|
|
|
32
30
|
/* Register handler for pressing send button. */
|
|
33
31
|
input.nextElementSibling.onclick =
|
|
@@ -20,11 +20,16 @@ class Plot {
|
|
|
20
20
|
/* Handle overlay events. */
|
|
21
21
|
if (this.overlay) {
|
|
22
22
|
/* Scroll, click and keyboard. */
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const options = {passive : true};
|
|
24
|
+
|
|
25
|
+
this.overlay.addEventListener(
|
|
26
|
+
"wheel", this.createEventSender(scrollEventKeys), options);
|
|
27
|
+
this.overlay.addEventListener(
|
|
28
|
+
"click", this.createEventSender(pointerEventKeys), options);
|
|
29
|
+
|
|
25
30
|
let eventHandler = this.createEventSender(keyboardEventKeys);
|
|
26
|
-
this.overlay.addEventListener("keydown", eventHandler);
|
|
27
|
-
this.overlay.addEventListener("keyup", eventHandler);
|
|
31
|
+
this.overlay.addEventListener("keydown", eventHandler, options);
|
|
32
|
+
this.overlay.addEventListener("keyup", eventHandler, options);
|
|
28
33
|
|
|
29
34
|
/* Should there be a keybind that opens this? */
|
|
30
35
|
// let plotButton = document.getElementById("runtimepy-plot-button");
|
|
@@ -15,34 +15,72 @@ class TabFilter {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
updateStyles(pattern) {
|
|
18
|
+
pattern = pattern.trim();
|
|
18
19
|
hash.setTabFilter(pattern);
|
|
19
20
|
|
|
20
21
|
if (!pattern) {
|
|
21
22
|
pattern = ".*";
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
|
|
25
|
+
let parts = pattern.split(/(\s+)/)
|
|
26
|
+
.filter((x) => x.trim().length > 0)
|
|
27
|
+
.map((x) => new RegExp(x));
|
|
24
28
|
|
|
25
29
|
for (let [name, elem] of Object.entries(this.buttons)) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
let found = elem.classList.contains("active");
|
|
31
|
+
|
|
32
|
+
if (!found) {
|
|
33
|
+
for (const re of parts) {
|
|
34
|
+
if (re.test(name)) {
|
|
35
|
+
found = true;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (found) {
|
|
42
|
+
for (const child of elem.parentElement.children) {
|
|
43
|
+
child.style.display = "block";
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
for (const child of elem.parentElement.children) {
|
|
47
|
+
child.style.display = "none";
|
|
48
|
+
}
|
|
30
49
|
}
|
|
31
50
|
}
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
keydown(event) {
|
|
54
|
+
// ctrl-l - go to channel table (open if needed) then close tabs
|
|
55
|
+
|
|
56
|
+
if (globalKeyEvent(event) || ignoreFilterKeyEvent(event)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let curr = this.input.value;
|
|
61
|
+
|
|
62
|
+
// new features: make '$' either auto-complete (one character per press)
|
|
63
|
+
// to the first non-selected tab currently appearing in filter and presses
|
|
64
|
+
// the nav button when it's the only button left
|
|
65
|
+
//
|
|
66
|
+
// tbd need another button for opening new tab, '@'?
|
|
35
67
|
if (event.key == "Enter") {
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
curr = "";
|
|
69
|
+
event.preventDefault();
|
|
38
70
|
} else {
|
|
39
|
-
let curr = this.input.value;
|
|
40
71
|
if (event.key == "Backspace") {
|
|
41
72
|
curr = curr.slice(0, -1);
|
|
42
73
|
} else {
|
|
43
74
|
curr += event.key;
|
|
44
75
|
}
|
|
45
|
-
this.updateStyles(curr);
|
|
46
76
|
}
|
|
77
|
+
|
|
78
|
+
if (!curr) {
|
|
79
|
+
this.input.value = curr;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.updateStyles(curr);
|
|
83
|
+
|
|
84
|
+
this.input.focus();
|
|
47
85
|
}
|
|
48
86
|
}
|
|
@@ -28,6 +28,8 @@ class TabInterface {
|
|
|
28
28
|
this.time = 0;
|
|
29
29
|
this.channelTimestamps = {};
|
|
30
30
|
this.channelColorButtons = {};
|
|
31
|
+
this.commandShown = true;
|
|
32
|
+
this.regularShown = true;
|
|
31
33
|
|
|
32
34
|
let table = this.query("tbody");
|
|
33
35
|
if (table) {
|
|
@@ -46,6 +48,9 @@ class TabInterface {
|
|
|
46
48
|
|
|
47
49
|
/* Audit vertical bar position. */
|
|
48
50
|
this.correctVerticalBarPosition();
|
|
51
|
+
|
|
52
|
+
/* Focus channel filter if it exists. */
|
|
53
|
+
this.focus();
|
|
49
54
|
}
|
|
50
55
|
this.worker.send({kind : msg});
|
|
51
56
|
} ];
|
|
@@ -61,6 +66,12 @@ class TabInterface {
|
|
|
61
66
|
((event) => { this.correctVerticalBarPosition(); }).bind(this));
|
|
62
67
|
}
|
|
63
68
|
|
|
69
|
+
focus() {
|
|
70
|
+
if (this.channelFilter) {
|
|
71
|
+
this.channelFilter.focus();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
initCommand() {
|
|
65
76
|
let command = this.query(`#${this.queryName}-command`);
|
|
66
77
|
if (command) {
|
|
@@ -82,15 +93,41 @@ class TabInterface {
|
|
|
82
93
|
}
|
|
83
94
|
|
|
84
95
|
updateChannelStyles(pattern) {
|
|
96
|
+
pattern = pattern.trim();
|
|
85
97
|
hash.handleChannelFilter(this.name, pattern);
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
let parts = pattern.split(/(\s+)/).filter((x) => x.trim().length > 0);
|
|
100
|
+
|
|
101
|
+
let showRegular = this.regularShown;
|
|
102
|
+
let special = "!";
|
|
103
|
+
if (parts.includes(special)) {
|
|
104
|
+
showRegular = false;
|
|
105
|
+
parts = parts.filter(e => e != special);
|
|
106
|
+
}
|
|
107
|
+
let showCommand = this.commandShown;
|
|
108
|
+
special = "@";
|
|
109
|
+
if (parts.includes(special)) {
|
|
110
|
+
showCommand = false;
|
|
111
|
+
parts = parts.filter(e => e != special);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!parts.length) {
|
|
115
|
+
parts.push(".*");
|
|
89
116
|
}
|
|
90
|
-
const re = new RegExp(pattern);
|
|
91
117
|
|
|
92
118
|
for (let [name, elem] of Object.entries(this.channelRows)) {
|
|
93
|
-
|
|
119
|
+
let found = false;
|
|
120
|
+
if ((showRegular && elem.classList.contains("channel-regular")) ||
|
|
121
|
+
(showCommand && elem.classList.contains("channel-commandable"))) {
|
|
122
|
+
for (const re of parts.map((x) => new RegExp(x))) {
|
|
123
|
+
if (re.test(name)) {
|
|
124
|
+
found = true;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (found) {
|
|
94
131
|
elem.style.display = "table-row";
|
|
95
132
|
} else {
|
|
96
133
|
elem.style.display = "none";
|
|
@@ -99,19 +136,34 @@ class TabInterface {
|
|
|
99
136
|
}
|
|
100
137
|
|
|
101
138
|
channelKeydown(event) {
|
|
139
|
+
// ctrl-h - go to tabs (open if necessary)
|
|
140
|
+
|
|
141
|
+
if (globalKeyEvent(event) || ignoreFilterKeyEvent(event)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let curr = this.channelFilter.value;
|
|
146
|
+
|
|
147
|
+
// new feature: make '$' toggle the state of all telemetry channels visible
|
|
148
|
+
// if any are already selected, enable all, if they're all already enabled
|
|
149
|
+
// disable them
|
|
102
150
|
if (event.key == "Enter") {
|
|
103
|
-
|
|
104
|
-
this.updateChannelStyles(this.channelFilter.value);
|
|
151
|
+
curr = "";
|
|
105
152
|
} else {
|
|
106
|
-
let curr = this.channelFilter.value;
|
|
107
153
|
if (event.key == "Backspace") {
|
|
108
154
|
curr = curr.slice(0, -1);
|
|
109
155
|
} else {
|
|
110
156
|
curr += event.key;
|
|
111
157
|
}
|
|
158
|
+
}
|
|
112
159
|
|
|
113
|
-
|
|
160
|
+
if (!curr) {
|
|
161
|
+
this.channelFilter.value = curr;
|
|
114
162
|
}
|
|
163
|
+
|
|
164
|
+
this.updateChannelStyles(curr);
|
|
165
|
+
|
|
166
|
+
this.focus();
|
|
115
167
|
}
|
|
116
168
|
|
|
117
169
|
initControls() {
|
|
@@ -175,6 +227,42 @@ class TabInterface {
|
|
|
175
227
|
if (selector && send) {
|
|
176
228
|
send.onclick = () => { this.worker.command(`custom ${selector.value}`); };
|
|
177
229
|
}
|
|
230
|
+
|
|
231
|
+
/* Initialize commandable/regular channel visibility toggle. */
|
|
232
|
+
let commandToggle = this.query("#toggle-command-channels");
|
|
233
|
+
if (commandToggle) {
|
|
234
|
+
commandToggle.onclick = () => {
|
|
235
|
+
this.commandShown = !this.commandShown;
|
|
236
|
+
let classes = commandToggle.children[0].classList;
|
|
237
|
+
if (this.commandShown) {
|
|
238
|
+
classes.toggle("bi-eye-slash");
|
|
239
|
+
classes.toggle("bi-eye");
|
|
240
|
+
classes.toggle("stale");
|
|
241
|
+
} else {
|
|
242
|
+
classes.toggle("bi-eye");
|
|
243
|
+
classes.toggle("bi-eye-slash");
|
|
244
|
+
classes.toggle("stale");
|
|
245
|
+
}
|
|
246
|
+
this.updateChannelStyles(this.channelFilter.value);
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
let regularToggle = this.query("#toggle-regular-channels");
|
|
250
|
+
if (regularToggle) {
|
|
251
|
+
regularToggle.onclick = () => {
|
|
252
|
+
this.regularShown = !this.regularShown;
|
|
253
|
+
let classes = regularToggle.children[0].classList;
|
|
254
|
+
if (this.regularShown) {
|
|
255
|
+
classes.toggle("bi-eye-slash-fill");
|
|
256
|
+
classes.toggle("bi-eye-fill");
|
|
257
|
+
classes.toggle("stale");
|
|
258
|
+
} else {
|
|
259
|
+
classes.toggle("bi-eye-slash-fill");
|
|
260
|
+
classes.toggle("bi-eye-fill");
|
|
261
|
+
classes.toggle("stale");
|
|
262
|
+
}
|
|
263
|
+
this.updateChannelStyles(this.channelFilter.value);
|
|
264
|
+
};
|
|
265
|
+
}
|
|
178
266
|
}
|
|
179
267
|
|
|
180
268
|
setHandler(elem) {
|
|
@@ -217,14 +305,14 @@ class TabInterface {
|
|
|
217
305
|
document.addEventListener(
|
|
218
306
|
up, (event) => { document.removeEventListener(move, handleMouse); },
|
|
219
307
|
{once : true});
|
|
220
|
-
});
|
|
308
|
+
}, {passive : true});
|
|
221
309
|
}
|
|
222
310
|
|
|
223
311
|
setupVerticalDivider(elem) {
|
|
224
312
|
setupCursorContext(elem, this.setupVerticalDividerEvents.bind(this));
|
|
225
313
|
}
|
|
226
314
|
|
|
227
|
-
correctVerticalBarPosition(
|
|
315
|
+
correctVerticalBarPosition() {
|
|
228
316
|
if (shown_tab == this.name && this.divider) {
|
|
229
317
|
let margin =
|
|
230
318
|
window.innerWidth - this.divider.getBoundingClientRect().right;
|
|
@@ -331,6 +419,13 @@ class TabInterface {
|
|
|
331
419
|
log(message) {
|
|
332
420
|
if (this.logs) {
|
|
333
421
|
this.logs.value += message + "\n";
|
|
422
|
+
|
|
423
|
+
const max_length = 4096;
|
|
424
|
+
if (this.logs.value.length > max_length) {
|
|
425
|
+
this.logs.value =
|
|
426
|
+
this.logs.value.substr(this.logs.value.length - max_length);
|
|
427
|
+
}
|
|
428
|
+
|
|
334
429
|
if (this.isShown()) {
|
|
335
430
|
this.logs.scrollTo(0, this.logs.scrollHeight);
|
|
336
431
|
}
|
|
@@ -384,7 +479,7 @@ class TabInterface {
|
|
|
384
479
|
this.clearPlotPoints();
|
|
385
480
|
break;
|
|
386
481
|
default:
|
|
387
|
-
console.log(`Action '${action}' not
|
|
482
|
+
console.log(`Action '${action}' not handled!`);
|
|
388
483
|
break;
|
|
389
484
|
}
|
|
390
485
|
}
|
|
@@ -16,6 +16,13 @@ class WindowHashManager {
|
|
|
16
16
|
tabClick(event) {
|
|
17
17
|
this.tabsShown = !this.tabsShown;
|
|
18
18
|
this.update();
|
|
19
|
+
|
|
20
|
+
/* Handle focus. */
|
|
21
|
+
if (!this.tabsShown && this.channelsShown && shown_tab in tabs) {
|
|
22
|
+
tabs[shown_tab].focus();
|
|
23
|
+
} else if (this.tabsShown && tabFilter) {
|
|
24
|
+
tabFilter.input.focus();
|
|
25
|
+
}
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
lightDarkClick(event) {
|
|
@@ -40,6 +47,13 @@ class WindowHashManager {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
this.update();
|
|
50
|
+
|
|
51
|
+
/* Handle focus. */
|
|
52
|
+
if (this.channelsShown && shown_tab in tabs) {
|
|
53
|
+
tabs[shown_tab].focus();
|
|
54
|
+
} else if (this.tabsShown && tabFilter) {
|
|
55
|
+
tabFilter.input.focus();
|
|
56
|
+
}
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
handleChannelFilter(tabName, value) {
|
|
@@ -111,9 +125,10 @@ class WindowHashManager {
|
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
/* Click handler for channels hide/show. */
|
|
114
|
-
|
|
115
|
-
if (channelsButton) {
|
|
116
|
-
channelsButton.addEventListener("click",
|
|
128
|
+
this.channelsButton = document.getElementById("channels-button");
|
|
129
|
+
if (this.channelsButton) {
|
|
130
|
+
this.channelsButton.addEventListener("click",
|
|
131
|
+
this.channelClick.bind(this));
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
/* Click handler for light/dark toggle. */
|
|
@@ -147,7 +162,7 @@ class WindowHashManager {
|
|
|
147
162
|
if (this.original) {
|
|
148
163
|
let boolsChannels = this.original.split("/");
|
|
149
164
|
let split = boolsChannels[0].split(",");
|
|
150
|
-
this.tab = split[0];
|
|
165
|
+
this.tab = decodeURI(split[0]);
|
|
151
166
|
|
|
152
167
|
/* Toggle plot-channel check boxes. */
|
|
153
168
|
for (let i = 1; i < boolsChannels.length; i++) {
|
|
@@ -155,12 +170,12 @@ class WindowHashManager {
|
|
|
155
170
|
for (let chan of nameChannels[1].split(",")) {
|
|
156
171
|
if (!chan.includes("=")) {
|
|
157
172
|
/* Handle regular channel names. */
|
|
158
|
-
this.togglePlotChannel(nameChannels[0], chan);
|
|
173
|
+
this.togglePlotChannel(nameChannels[0], decodeURI(chan));
|
|
159
174
|
} else {
|
|
160
175
|
/* Handle key-value pairs. */
|
|
161
176
|
let keyVal = chan.split("=");
|
|
162
177
|
if (keyVal.length == 2 && keyVal[0] == "filter" && keyVal[1]) {
|
|
163
|
-
this.setTabChannelFilter(nameChannels[0], keyVal[1]);
|
|
178
|
+
this.setTabChannelFilter(nameChannels[0], decodeURI(keyVal[1]));
|
|
164
179
|
}
|
|
165
180
|
}
|
|
166
181
|
}
|
|
@@ -172,7 +187,7 @@ class WindowHashManager {
|
|
|
172
187
|
let keyVal = item.split("=");
|
|
173
188
|
if (keyVal.length == 2) {
|
|
174
189
|
if (keyVal[0] == "filter" && keyVal[1]) {
|
|
175
|
-
this.updateTabFilter(keyVal[1]);
|
|
190
|
+
this.updateTabFilter(decodeURI(keyVal[1]));
|
|
176
191
|
}
|
|
177
192
|
if (keyVal[0] == "min-tx-period-ms") {
|
|
178
193
|
this.minTxPeriod = Number(keyVal[1]);
|
|
@@ -181,13 +196,13 @@ class WindowHashManager {
|
|
|
181
196
|
}
|
|
182
197
|
}
|
|
183
198
|
|
|
184
|
-
if (split.includes("hide-tabs")) {
|
|
199
|
+
if (tabButton && split.includes("hide-tabs")) {
|
|
185
200
|
tabButton.click();
|
|
186
201
|
}
|
|
187
|
-
if (split.includes("hide-channels")) {
|
|
188
|
-
channelsButton.click();
|
|
202
|
+
if (this.channelsButton && split.includes("hide-channels")) {
|
|
203
|
+
this.channelsButton.click();
|
|
189
204
|
}
|
|
190
|
-
if (split.includes("light-mode")) {
|
|
205
|
+
if (lightDarkButton && split.includes("light-mode")) {
|
|
191
206
|
lightDarkButton.click();
|
|
192
207
|
}
|
|
193
208
|
}
|
|
@@ -210,7 +225,7 @@ class WindowHashManager {
|
|
|
210
225
|
let hash = this.tab;
|
|
211
226
|
|
|
212
227
|
if (this.tabFilter) {
|
|
213
|
-
hash += ",filter=" + this.tabFilter;
|
|
228
|
+
hash += ",filter=" + encodeURI(this.tabFilter);
|
|
214
229
|
}
|
|
215
230
|
if (!this.tabsShown) {
|
|
216
231
|
hash += ",hide-tabs"
|
|
@@ -236,14 +251,14 @@ class WindowHashManager {
|
|
|
236
251
|
for (let name in channels) {
|
|
237
252
|
if (channels[name]) {
|
|
238
253
|
if (firstChan) {
|
|
239
|
-
hash += "/" + tab + ":"
|
|
254
|
+
hash += "/" + encodeURI(tab) + ":"
|
|
240
255
|
firstChan = false;
|
|
241
256
|
}
|
|
242
257
|
|
|
243
258
|
if (hash.slice(-1) != ":") {
|
|
244
259
|
hash += ",";
|
|
245
260
|
}
|
|
246
|
-
hash += name;
|
|
261
|
+
hash += encodeURI(name);
|
|
247
262
|
}
|
|
248
263
|
}
|
|
249
264
|
}
|
|
@@ -258,7 +273,7 @@ class WindowHashManager {
|
|
|
258
273
|
if (hash.slice(-1) != ":") {
|
|
259
274
|
hash += ",";
|
|
260
275
|
}
|
|
261
|
-
hash += "filter=" + this.filters[tab];
|
|
276
|
+
hash += "filter=" + encodeURI(this.filters[tab]);
|
|
262
277
|
}
|
|
263
278
|
}
|
|
264
279
|
|
runtimepy/data/js/init.js
CHANGED
|
@@ -40,13 +40,30 @@ function setupCursorMove(elem, down, move, up, handleMove) {
|
|
|
40
40
|
document.removeEventListener(move, handleMove);
|
|
41
41
|
elem.classList.remove("moving");
|
|
42
42
|
}, {once : true});
|
|
43
|
-
});
|
|
43
|
+
}, {passive : true});
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function randomHexColor() {
|
|
47
47
|
return "#" + (Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, "0");
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function isModifierKeyEvent(event) {
|
|
51
|
+
return event.key == "Shift" || event.key == "Control" ||
|
|
52
|
+
event.key == "Meta" || event.key == "Alt";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ignoreFilterKeyEvent(event) {
|
|
56
|
+
// home end pg up pg down delete f keys
|
|
57
|
+
return isModifierKeyEvent(event) || event.key == "Tab";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function globalKeyEvent(event) {
|
|
61
|
+
// (common)
|
|
62
|
+
// keybind for dedent (ctrl-d?) + expand (ctrl-e?)
|
|
63
|
+
// toggle tabs + toggle channel table
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
/* Load settings control mappings. */
|
|
51
68
|
let settings = {};
|
|
52
69
|
|
|
@@ -25,4 +25,14 @@ window.onload = () => {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
bootstrap_init();
|
|
28
|
+
|
|
29
|
+
/* Hide button column for print mode. */
|
|
30
|
+
const params = new URLSearchParams(window.location.search);
|
|
31
|
+
if (params.get("print") == "true") {
|
|
32
|
+
const elem = document.getElementById("button-column");
|
|
33
|
+
if (elem) {
|
|
34
|
+
elem.classList.remove("d-flex");
|
|
35
|
+
elem.style.display = "none";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
28
38
|
};
|