websocket-text-relay 1.1.5 → 1.1.6
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.
- package/docs/code-structure.md +15 -11
- package/eslint.config.js +5 -1
- package/package.json +9 -9
- package/src/ui/css/main.css +22 -78
- package/src/ui/favicon.png +0 -0
- package/src/ui/index.html +19 -14
- package/src/ui/js/components/activityLabels.js +41 -0
- package/src/ui/js/components/activityTimeSeries.js +58 -0
- package/src/ui/js/components/drawSessionLabel.js +117 -0
- package/src/ui/js/components/footerStatus.js +37 -0
- package/src/ui/js/components/headers.js +100 -0
- package/src/ui/js/components/sessionWedges.js +96 -0
- package/src/ui/js/components/statusRing.js +51 -0
- package/src/ui/js/data/wtrActivity.js +85 -0
- package/src/ui/js/data/wtrActivity.types.js +3 -0
- package/src/ui/js/data/wtrStatus.js +134 -0
- package/src/ui/js/data/wtrStatus.types.js +61 -0
- package/src/ui/js/{util → setup}/EventEmitter.js +5 -1
- package/src/ui/js/{util → setup}/WebsocketClient.js +1 -4
- package/src/ui/js/setup/dependencyManager.js +9 -0
- package/src/ui/js/setup/evalOnChange.js +18 -0
- package/src/ui/js/setup/eventSubscriber.js +21 -0
- package/src/ui/js/setup.js +141 -0
- package/src/ui/js/util/constants.js +5 -1
- package/src/ui/js/util/drawing.js +26 -76
- package/src/websocket-interface/httpServer.js +1 -1
- package/src/ui/js/components/ActivityTimeseriesGraph.js +0 -194
- package/src/ui/js/components/HeaderSummary.js +0 -22
- package/src/ui/js/components/ServerStatus.js +0 -43
- package/src/ui/js/components/SessionLabels.js +0 -319
- package/src/ui/js/components/SessionWedges.js +0 -127
- package/src/ui/js/components/StatusRing.js +0 -54
- package/src/ui/js/components/grids.js +0 -36
- package/src/ui/js/index.js +0 -121
- package/src/ui/js/main.js +0 -128
- package/src/ui/js/util/DependencyManager.js +0 -31
package/docs/code-structure.md
CHANGED
|
@@ -87,21 +87,27 @@ The root of the static site is the `src/ui` directory.
|
|
|
87
87
|
The UI is entirely made up of SVG elements, so the only thing the index.html file has to do is set up the root
|
|
88
88
|
SVG element with some groups to act as containers for the different components to use.
|
|
89
89
|
|
|
90
|
-
### js/
|
|
90
|
+
### js/setup.js
|
|
91
91
|
|
|
92
92
|
- Sets up the websocket-text-relay client with handlers for css and javascript files.
|
|
93
93
|
- initializes the simple dependency management system that allows the UI to be split into several javascript files.
|
|
94
|
-
- Hooks up events to handle resizing the SVG element on window resize.
|
|
95
94
|
- emit data and activity events that the UI components can hook into
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
### js/util/DependencyManager.js
|
|
96
|
+
### js/setup/dependencyManager.js
|
|
100
97
|
|
|
101
98
|
This is a very quick and simple dependency management system. The dependency container is just an object in the global scope.
|
|
102
99
|
An exportDeps function is created to make it easy to specify which objects in scope are to be exported.
|
|
103
100
|
|
|
104
|
-
|
|
101
|
+
### js/setup/eventSubscriber.js
|
|
102
|
+
|
|
103
|
+
Provides the onEvent function that each file can use when subscribing to events. The events are automatically
|
|
104
|
+
unsubscribed when that file gets reevaluated. See the `js/components/sessionWedges.js` file for an example of usage.
|
|
105
|
+
|
|
106
|
+
### js/setup/evalOnChange.js
|
|
107
|
+
|
|
108
|
+
This provides a function where you can define what files should be run after the current file is updated.
|
|
109
|
+
Rather than calculate a dependency graph and try to automatically rerun files, simply define the behavior you want
|
|
110
|
+
while you are editing the file. See the `js/util/constants.js` file for an example of usage.
|
|
105
111
|
|
|
106
112
|
### js/util/drawing.js
|
|
107
113
|
|
|
@@ -115,10 +121,8 @@ The center of the UI is at (0, 0) with a minimum height and width of 2. Having a
|
|
|
115
121
|
|
|
116
122
|
### js/components/
|
|
117
123
|
|
|
118
|
-
The components directory contains the javascript
|
|
119
|
-
|
|
120
|
-
Each class handles the state for its component and has a draw function that renders it to the screen.
|
|
124
|
+
The components directory contains the javascript files that render the different elements on the screen.
|
|
121
125
|
|
|
122
|
-
|
|
126
|
+
### js/data/
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
Read data from the websocket server and construct data stores and events required by the components.
|
package/eslint.config.js
CHANGED
|
@@ -2,12 +2,16 @@ import js from "@eslint/js"
|
|
|
2
2
|
import globals from "globals"
|
|
3
3
|
import { defineConfig } from "eslint/config"
|
|
4
4
|
|
|
5
|
+
const wtrGlobals = {
|
|
6
|
+
__WTR__: "readonly",
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
export default defineConfig([
|
|
6
10
|
{
|
|
7
11
|
files: ["**/*.{js,mjs,cjs}"],
|
|
8
12
|
plugins: { js },
|
|
9
13
|
extends: ["js/recommended"],
|
|
10
|
-
languageOptions: { globals: { ...globals.node, ...globals.browser } },
|
|
14
|
+
languageOptions: { globals: { ...globals.node, ...globals.browser, ...wtrGlobals } },
|
|
11
15
|
},
|
|
12
16
|
{
|
|
13
17
|
rules: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "websocket-text-relay",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "An LSP server for sending live file updates from your text editor to the front end via websockets.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"websocket-text-relay": "./start.js"
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/niels4/websocket-text-relay#readme",
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@eslint/js": "
|
|
40
|
-
"@vitest/coverage-v8": "
|
|
41
|
-
"@vitest/ui": "
|
|
42
|
-
"eslint": "
|
|
43
|
-
"globals": "
|
|
44
|
-
"prettier": "3.
|
|
45
|
-
"vitest": "
|
|
39
|
+
"@eslint/js": "9.39.2",
|
|
40
|
+
"@vitest/coverage-v8": "4.0.16",
|
|
41
|
+
"@vitest/ui": "4.0.16",
|
|
42
|
+
"eslint": "9.39.2",
|
|
43
|
+
"globals": "16.5.0",
|
|
44
|
+
"prettier": "3.7.4",
|
|
45
|
+
"vitest": "4.0.16"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"ws": "
|
|
48
|
+
"ws": "8.18.3"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/src/ui/css/main.css
CHANGED
|
@@ -9,16 +9,12 @@
|
|
|
9
9
|
--color-evening-shadow: #514a45;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
.color_palette {
|
|
13
|
-
--bg2: var(--color-navy-nightfall);
|
|
14
|
-
--accent-active: var(var(--color-golden-sunset));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
12
|
.color_theme {
|
|
18
|
-
--
|
|
13
|
+
--accent-active: var(var(--color-golden-sunset));
|
|
14
|
+
--main-background-color: var(--color-navy-nightfall);
|
|
19
15
|
--header-text-color: var(--color-ivory-daybreak);
|
|
20
16
|
--summary-value-text-color: hsl(from var(--header-text-color) h s l / 0.9);
|
|
21
|
-
--offline-color: oklch(from var(--color-
|
|
17
|
+
--offline-color: oklch(from var(--color-tangerine-sunset) calc(l * 0.4) calc(c * 0.1) h);
|
|
22
18
|
--online-color: oklch(from var(--color-golden-sunset) calc(l * 0.8) calc(c * 0.25) h);
|
|
23
19
|
--active-color: var(--color-golden-sunset);
|
|
24
20
|
--wedge-online-color: oklch(from var(--color-azure-afternoon) calc(l * 0.5) calc(c * 0.5) h);
|
|
@@ -40,55 +36,33 @@ body {
|
|
|
40
36
|
font-size: 0.1pt;
|
|
41
37
|
}
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
stroke: #fff;
|
|
45
|
-
stroke-width: 0.001;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.grid_axis {
|
|
49
|
-
stroke-width: 0.003;
|
|
50
|
-
stroke: hsl(76, 90%, 80%);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
#header_summary_group {
|
|
39
|
+
.header {
|
|
54
40
|
fill: var(--header-text-color);
|
|
55
41
|
font-weight: 300;
|
|
42
|
+
font-size: 0.1pt;
|
|
56
43
|
}
|
|
57
44
|
|
|
58
45
|
.right_header {
|
|
59
46
|
text-anchor: end;
|
|
60
47
|
}
|
|
61
48
|
|
|
62
|
-
.
|
|
63
|
-
font-weight: 100;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
#status_ring_group {
|
|
49
|
+
.status_ring_wrapper.offline {
|
|
67
50
|
stroke: var(--offline-color);
|
|
68
51
|
}
|
|
69
52
|
|
|
70
|
-
|
|
53
|
+
.status_ring_wrapper.online {
|
|
71
54
|
stroke: var(--online-color);
|
|
72
55
|
}
|
|
73
56
|
|
|
74
|
-
|
|
57
|
+
.status_ring_wrapper.active {
|
|
75
58
|
stroke: var(--active-color);
|
|
76
59
|
}
|
|
77
60
|
|
|
78
|
-
.
|
|
79
|
-
display: none;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.single_wedge_group.online,
|
|
83
|
-
.single_wedge_group.active {
|
|
84
|
-
display: inherit;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.online .wedge_node {
|
|
61
|
+
.wedge_node {
|
|
88
62
|
fill: var(--wedge-online-color);
|
|
89
63
|
}
|
|
90
64
|
|
|
91
|
-
.active
|
|
65
|
+
.wedge_node.active {
|
|
92
66
|
fill: var(--wedge-active-color);
|
|
93
67
|
}
|
|
94
68
|
|
|
@@ -115,57 +89,27 @@ body {
|
|
|
115
89
|
fill: var(--active-color);
|
|
116
90
|
}
|
|
117
91
|
|
|
118
|
-
.
|
|
119
|
-
/* fill: #ffffff1f; */
|
|
120
|
-
fill: none;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.timeseries_path {
|
|
92
|
+
.time_series_path {
|
|
124
93
|
stroke: var(--timeseries-line-color);
|
|
125
|
-
|
|
94
|
+
stroke-width: 3;
|
|
95
|
+
vector-effect: non-scaling-stroke;
|
|
96
|
+
shape-rendering: crispEdges;
|
|
126
97
|
}
|
|
127
98
|
|
|
128
|
-
.
|
|
129
|
-
/* fill: var(--timeseries-label-color); */
|
|
130
|
-
font-size: 0.04pt;
|
|
131
|
-
font-weight: 400;
|
|
132
|
-
text-anchor: end;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.timeseries_small_label {
|
|
136
|
-
/* fill: var(--timeseries-label-color); */
|
|
99
|
+
.small_label {
|
|
137
100
|
font-size: 0.03pt;
|
|
138
101
|
font-weight: 400;
|
|
139
|
-
|
|
102
|
+
fill: var(--timeseries-label-color);
|
|
103
|
+
text-anchor: middle;
|
|
104
|
+
dominant-baseline: central;
|
|
140
105
|
}
|
|
141
106
|
|
|
142
|
-
.
|
|
107
|
+
.time_series_value {
|
|
143
108
|
fill: var(--timeseries-value-color);
|
|
144
109
|
font-size: 0.05pt;
|
|
145
110
|
font-weight: 300;
|
|
146
111
|
text-anchor: middle;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.tooltip_text {
|
|
150
|
-
fill: #fff;
|
|
151
|
-
font-size: 0.025pt;
|
|
152
|
-
font-weight: 200;
|
|
153
|
-
text-anchor: middle;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.tooltip_outline {
|
|
157
|
-
stroke: #fff;
|
|
158
|
-
stroke-width: 0.002;
|
|
159
|
-
fill: #000;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.tooltip_display_group {
|
|
163
|
-
opacity: 0;
|
|
164
|
-
transition: opacity 0.35s ease;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.tooltip_wrapper_group:hover .tooltip_display_group {
|
|
168
|
-
opacity: 1;
|
|
112
|
+
dominant-baseline: central;
|
|
169
113
|
}
|
|
170
114
|
|
|
171
115
|
.server_status_label {
|
|
@@ -189,11 +133,11 @@ body {
|
|
|
189
133
|
text-anchor: middle;
|
|
190
134
|
}
|
|
191
135
|
|
|
192
|
-
.
|
|
136
|
+
.wedge_label_dot {
|
|
193
137
|
fill: #fff;
|
|
194
138
|
}
|
|
195
139
|
|
|
196
|
-
.
|
|
140
|
+
.wedge_label_line {
|
|
197
141
|
stroke: #fff;
|
|
198
142
|
stroke-width: 0.005;
|
|
199
143
|
}
|
|
Binary file
|
package/src/ui/index.html
CHANGED
|
@@ -6,31 +6,36 @@
|
|
|
6
6
|
|
|
7
7
|
<title>WTR Status</title>
|
|
8
8
|
|
|
9
|
+
<link rel="icon" type="image/png" href="favicon.png" />
|
|
9
10
|
<link rel="stylesheet" href="css/fonts.css" />
|
|
10
11
|
<style id="main_style"></style>
|
|
11
|
-
<script type="module" src="js/
|
|
12
|
+
<script type="module" src="js/setup.js" defer></script>
|
|
12
13
|
|
|
13
14
|
<style>
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
html {
|
|
16
|
+
height: 100dvh;
|
|
16
17
|
overscroll-behavior: none;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#svg_root {
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 0;
|
|
23
|
+
left: 0;
|
|
24
|
+
height: 100dvh;
|
|
25
|
+
width: 100dvw;
|
|
21
26
|
}
|
|
22
27
|
</style>
|
|
23
28
|
</head>
|
|
24
29
|
|
|
25
|
-
<body class="base_colors
|
|
30
|
+
<body class="base_colors color_theme">
|
|
26
31
|
<svg id="svg_root" viewBox="-1 -1 2 2" xmlns="http://www.w3.org/2000/svg">
|
|
27
|
-
<g id="
|
|
28
|
-
<g id="header_summary_group"></g>
|
|
29
|
-
<g id="status_ring_group"></g>
|
|
30
|
-
<g id="client_wedges_group" class="wedge_group"></g>
|
|
32
|
+
<g id="headers_group"></g>
|
|
31
33
|
<g id="editor_wedges_group" class="wedge_group"></g>
|
|
32
|
-
<g id="
|
|
33
|
-
<g id="
|
|
34
|
+
<g id="client_wedges_group" class="wedge_group"></g>
|
|
35
|
+
<g id="activity_time_series_group"></g>
|
|
36
|
+
<g id="activity_labels_group"></g>
|
|
37
|
+
<g id="status_ring_group"></g>
|
|
38
|
+
<g id="footer_status_group"></g>
|
|
34
39
|
</svg>
|
|
35
40
|
</body>
|
|
36
41
|
</html>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const { drawText, wtrActivityDataWindowEmitter, onEvent, constants } = __WTR__
|
|
2
|
+
const { innerRingRadius } = constants
|
|
3
|
+
|
|
4
|
+
const parentGroup = document.getElementById("activity_labels_group")
|
|
5
|
+
parentGroup.innerHTML = ""
|
|
6
|
+
|
|
7
|
+
const valuePadding = 0.068
|
|
8
|
+
const labelPadding = 0.14
|
|
9
|
+
|
|
10
|
+
drawText({
|
|
11
|
+
text: "Max",
|
|
12
|
+
y: -innerRingRadius + labelPadding,
|
|
13
|
+
className: "small_label",
|
|
14
|
+
parent: parentGroup,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const maxValueText = drawText({
|
|
18
|
+
text: "0",
|
|
19
|
+
y: -innerRingRadius + valuePadding,
|
|
20
|
+
className: "time_series_value",
|
|
21
|
+
parent: parentGroup,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
drawText({
|
|
25
|
+
text: "Updates / second",
|
|
26
|
+
y: innerRingRadius - labelPadding,
|
|
27
|
+
className: "small_label",
|
|
28
|
+
parent: parentGroup,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const currentValueText = drawText({
|
|
32
|
+
text: "0",
|
|
33
|
+
y: innerRingRadius - valuePadding,
|
|
34
|
+
className: "time_series_value",
|
|
35
|
+
parent: parentGroup,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
onEvent(wtrActivityDataWindowEmitter, "data", ({ maxValue, currentValue }) => {
|
|
39
|
+
maxValueText.textContent = maxValue
|
|
40
|
+
currentValueText.textContent = currentValue
|
|
41
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const { drawSvgElement, constants, wtrActivityDataWindowEmitter, onEvent } = __WTR__
|
|
2
|
+
const { innerRingRadius, dataWindowSize } = constants
|
|
3
|
+
|
|
4
|
+
const parentGroup = document.getElementById("activity_time_series_group")
|
|
5
|
+
parentGroup.innerHTML = ""
|
|
6
|
+
|
|
7
|
+
const animationProps = { duration: 1000, easing: "linear", iterations: 1, fill: "forwards" }
|
|
8
|
+
|
|
9
|
+
const minX = -innerRingRadius
|
|
10
|
+
const width = innerRingRadius * 2
|
|
11
|
+
const height = 0.25
|
|
12
|
+
const maxY = height / 2
|
|
13
|
+
const intervalWidth = width / (dataWindowSize - 2)
|
|
14
|
+
|
|
15
|
+
const clipId = "time-series-clip"
|
|
16
|
+
|
|
17
|
+
const clipPath = drawSvgElement({
|
|
18
|
+
tag: "clipPath",
|
|
19
|
+
attributes: { id: clipId },
|
|
20
|
+
parent: parentGroup,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
drawSvgElement({
|
|
24
|
+
tag: "circle",
|
|
25
|
+
attributes: { r: innerRingRadius },
|
|
26
|
+
parent: clipPath,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const clippedGroup = drawSvgElement({
|
|
30
|
+
tag: "g",
|
|
31
|
+
attributes: { "clip-path": `url(#${clipId})` },
|
|
32
|
+
parent: parentGroup,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const valuePath = drawSvgElement({
|
|
36
|
+
tag: "path",
|
|
37
|
+
attributes: { d: "" },
|
|
38
|
+
className: "time_series_path",
|
|
39
|
+
parent: clippedGroup,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const getValueScale = (maxValue) => (maxValue === 0 ? 0.00001 : height / maxValue)
|
|
43
|
+
|
|
44
|
+
onEvent(wtrActivityDataWindowEmitter, "data", (data) => {
|
|
45
|
+
valuePath.setAttribute("d", data.path)
|
|
46
|
+
const prevValueScale = getValueScale(data.prevMaxValue)
|
|
47
|
+
const valueScale = getValueScale(data.maxValue)
|
|
48
|
+
|
|
49
|
+
const animationKeyFrames = [
|
|
50
|
+
{
|
|
51
|
+
transform: `translateX(${minX}px) translateY(${maxY}px) scaleX(${intervalWidth}) scaleY(${-prevValueScale})`,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
transform: `translateX(${minX - intervalWidth}px) translateY(${maxY}px) scaleX(${intervalWidth}) scaleY(${-valueScale})`,
|
|
55
|
+
},
|
|
56
|
+
]
|
|
57
|
+
valuePath.animate(animationKeyFrames, animationProps)
|
|
58
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const {
|
|
2
|
+
drawSvgElement,
|
|
3
|
+
drawPolarCircle,
|
|
4
|
+
drawPolarLine,
|
|
5
|
+
drawText,
|
|
6
|
+
polarToCartesian,
|
|
7
|
+
exportDeps,
|
|
8
|
+
evalOnChange,
|
|
9
|
+
} = __WTR__
|
|
10
|
+
|
|
11
|
+
evalOnChange(["js/components/sessionWedges.js"])
|
|
12
|
+
|
|
13
|
+
const labelLineDistance = 0.07
|
|
14
|
+
const underlinePadding = 0.02
|
|
15
|
+
const summaryCircleRadius = 0.014
|
|
16
|
+
const summaryValueSpacing = 0.1
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {{session: EditorStatus | ClientStatus, direction: 1 | -1}}
|
|
20
|
+
*/
|
|
21
|
+
const drawSessionLabel = ({ wedgeCenterAngle, wedgeCenterRadius, session, direction, parent }) => {
|
|
22
|
+
drawPolarCircle({
|
|
23
|
+
angle: wedgeCenterAngle,
|
|
24
|
+
radius: wedgeCenterRadius,
|
|
25
|
+
r: 0.01,
|
|
26
|
+
className: "wedge_label_dot",
|
|
27
|
+
parent,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const textStartRadius = wedgeCenterRadius + labelLineDistance
|
|
31
|
+
drawPolarLine({
|
|
32
|
+
startAngle: wedgeCenterAngle,
|
|
33
|
+
startRadius: wedgeCenterRadius,
|
|
34
|
+
endAngle: wedgeCenterAngle,
|
|
35
|
+
endRadius: textStartRadius,
|
|
36
|
+
className: "wedge_label_line",
|
|
37
|
+
parent,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const serverIndicator = session.isServer ? "* " : ""
|
|
41
|
+
const [textStartX, textStartY] = polarToCartesian(wedgeCenterAngle, textStartRadius)
|
|
42
|
+
const nameTextNode = drawText({
|
|
43
|
+
x: textStartX,
|
|
44
|
+
y: textStartY - underlinePadding,
|
|
45
|
+
text: serverIndicator + session.name,
|
|
46
|
+
textAnchor: direction === 1 ? "end" : "start",
|
|
47
|
+
className: "wedge_identifier",
|
|
48
|
+
parent,
|
|
49
|
+
})
|
|
50
|
+
const nameTextBBox = nameTextNode.getBBox()
|
|
51
|
+
|
|
52
|
+
const underlineX2 = direction === 1 ? nameTextBBox.x : nameTextBBox.x + nameTextBBox.width
|
|
53
|
+
drawSvgElement({
|
|
54
|
+
tag: "line",
|
|
55
|
+
attributes: { x1: textStartX, y1: textStartY, x2: underlineX2, y2: textStartY },
|
|
56
|
+
className: "wedge_label_line",
|
|
57
|
+
parent,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const summaryGroup = drawSvgElement({ tag: "g", parent })
|
|
61
|
+
|
|
62
|
+
let currentSummaryX = textStartX
|
|
63
|
+
|
|
64
|
+
const { leftText, rightText } =
|
|
65
|
+
direction === 1
|
|
66
|
+
? { leftText: session.openCount, rightText: session.activeOpenCount }
|
|
67
|
+
: { leftText: session.watchCount, rightText: session.activeWatchCount }
|
|
68
|
+
|
|
69
|
+
drawText({
|
|
70
|
+
x: currentSummaryX + summaryCircleRadius * 2,
|
|
71
|
+
y: textStartY,
|
|
72
|
+
text: leftText,
|
|
73
|
+
dominantBaseline: "middle",
|
|
74
|
+
className: "summary_text_value",
|
|
75
|
+
parent: summaryGroup,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
drawSvgElement({
|
|
79
|
+
tag: "circle",
|
|
80
|
+
attributes: {
|
|
81
|
+
cx: currentSummaryX,
|
|
82
|
+
cy: textStartY - summaryCircleRadius / 2,
|
|
83
|
+
r: summaryCircleRadius,
|
|
84
|
+
},
|
|
85
|
+
className: "summary_watched_circle",
|
|
86
|
+
parent: summaryGroup,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
currentSummaryX += summaryValueSpacing
|
|
90
|
+
|
|
91
|
+
drawText({
|
|
92
|
+
x: currentSummaryX + summaryCircleRadius * 2,
|
|
93
|
+
y: textStartY,
|
|
94
|
+
text: rightText,
|
|
95
|
+
dominantBaseline: "middle",
|
|
96
|
+
className: "summary_text_value",
|
|
97
|
+
parent: summaryGroup,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
drawSvgElement({
|
|
101
|
+
tag: "circle",
|
|
102
|
+
attributes: {
|
|
103
|
+
cx: currentSummaryX,
|
|
104
|
+
cy: textStartY - summaryCircleRadius / 2,
|
|
105
|
+
r: summaryCircleRadius,
|
|
106
|
+
},
|
|
107
|
+
className: "summary_active_circle",
|
|
108
|
+
parent: summaryGroup,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const summaryGroupBBox = summaryGroup.getBBox()
|
|
112
|
+
const translateX = direction === 1 ? -summaryGroupBBox.width - 0.02 : 0.05
|
|
113
|
+
const translateY = summaryGroupBBox.height / 2 + 0.014
|
|
114
|
+
summaryGroup.setAttribute("transform", `translate(${translateX}, ${translateY})`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
exportDeps({ drawSessionLabel })
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const { drawText, wtrStatusEmitter, onEvent } = __WTR__
|
|
2
|
+
|
|
3
|
+
const valueTextClass = "server_status_value"
|
|
4
|
+
const offlineTextClass = "server_status_offline"
|
|
5
|
+
|
|
6
|
+
const parentGroup = document.getElementById("footer_status_group")
|
|
7
|
+
parentGroup.innerHTML = ""
|
|
8
|
+
|
|
9
|
+
drawText({
|
|
10
|
+
x: 0,
|
|
11
|
+
y: 0.85,
|
|
12
|
+
text: "WS Server PID",
|
|
13
|
+
className: "server_status_label",
|
|
14
|
+
parent: parentGroup,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const pidElement = drawText({ x: 0, y: 0.748, text: "-1", parent: parentGroup })
|
|
18
|
+
|
|
19
|
+
const offlineElement = drawText({
|
|
20
|
+
x: 0,
|
|
21
|
+
y: 0.748,
|
|
22
|
+
text: "OFFLINE",
|
|
23
|
+
className: offlineTextClass,
|
|
24
|
+
parent: parentGroup,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
onEvent(wtrStatusEmitter, "data", (/** @type {WtrStatus} */ data) => {
|
|
28
|
+
const server = data.editors.find((editor) => editor.isServer)
|
|
29
|
+
if (!data.isOnline || server == null) {
|
|
30
|
+
pidElement.classList.remove(valueTextClass)
|
|
31
|
+
offlineElement.classList.add(offlineTextClass)
|
|
32
|
+
} else {
|
|
33
|
+
pidElement.textContent = server.lsPid
|
|
34
|
+
pidElement.classList.add(valueTextClass)
|
|
35
|
+
offlineElement.classList.remove(offlineTextClass)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const { drawText, drawSvgElement } = __WTR__
|
|
2
|
+
|
|
3
|
+
const parentGroup = document.getElementById("headers_group")
|
|
4
|
+
parentGroup.innerHTML = ""
|
|
5
|
+
|
|
6
|
+
const legendCircleRadius = 0.014
|
|
7
|
+
|
|
8
|
+
const baseLineY = -0.73
|
|
9
|
+
const xOffset = 0.86
|
|
10
|
+
const legendY = baseLineY + 0.05
|
|
11
|
+
|
|
12
|
+
// left header
|
|
13
|
+
drawText({ x: -xOffset, y: baseLineY, text: "editors", className: "header", parent: parentGroup })
|
|
14
|
+
|
|
15
|
+
// left legend
|
|
16
|
+
let circleStart = -xOffset + 0.093
|
|
17
|
+
let labelStart = circleStart + legendCircleRadius * 2
|
|
18
|
+
|
|
19
|
+
drawSvgElement({
|
|
20
|
+
tag: "circle",
|
|
21
|
+
className: "summary_watched_circle",
|
|
22
|
+
attributes: { r: legendCircleRadius, cx: circleStart, cy: legendY },
|
|
23
|
+
parent: parentGroup,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
drawText({
|
|
27
|
+
text: "Open",
|
|
28
|
+
textAnchor: "start",
|
|
29
|
+
className: "small_label",
|
|
30
|
+
x: labelStart,
|
|
31
|
+
y: legendY,
|
|
32
|
+
parent: parentGroup,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
circleStart += 0.1822
|
|
36
|
+
labelStart = circleStart + legendCircleRadius * 2
|
|
37
|
+
|
|
38
|
+
drawSvgElement({
|
|
39
|
+
tag: "circle",
|
|
40
|
+
className: "summary_active_circle",
|
|
41
|
+
attributes: { r: legendCircleRadius, cx: circleStart, cy: legendY },
|
|
42
|
+
parent: parentGroup,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
drawText({
|
|
46
|
+
text: "Active",
|
|
47
|
+
textAnchor: "start",
|
|
48
|
+
className: "small_label",
|
|
49
|
+
x: labelStart,
|
|
50
|
+
y: legendY,
|
|
51
|
+
parent: parentGroup,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// right header
|
|
55
|
+
drawText({
|
|
56
|
+
x: xOffset,
|
|
57
|
+
y: baseLineY,
|
|
58
|
+
text: "clients",
|
|
59
|
+
className: ["header", "right_header"],
|
|
60
|
+
parent: parentGroup,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// right legend
|
|
64
|
+
circleStart = xOffset - 0.3914
|
|
65
|
+
labelStart = circleStart + legendCircleRadius * 2
|
|
66
|
+
|
|
67
|
+
drawSvgElement({
|
|
68
|
+
tag: "circle",
|
|
69
|
+
className: "summary_watched_circle",
|
|
70
|
+
attributes: { r: legendCircleRadius, cx: circleStart, cy: legendY },
|
|
71
|
+
parent: parentGroup,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
drawText({
|
|
75
|
+
text: "Watch",
|
|
76
|
+
textAnchor: "start",
|
|
77
|
+
className: "small_label",
|
|
78
|
+
x: labelStart,
|
|
79
|
+
y: legendY,
|
|
80
|
+
parent: parentGroup,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
circleStart += 0.1952
|
|
84
|
+
labelStart = circleStart + legendCircleRadius * 2
|
|
85
|
+
|
|
86
|
+
drawSvgElement({
|
|
87
|
+
tag: "circle",
|
|
88
|
+
className: "summary_active_circle",
|
|
89
|
+
attributes: { r: legendCircleRadius, cx: circleStart, cy: legendY },
|
|
90
|
+
parent: parentGroup,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
drawText({
|
|
94
|
+
text: "Active",
|
|
95
|
+
textAnchor: "start",
|
|
96
|
+
className: "small_label",
|
|
97
|
+
x: labelStart,
|
|
98
|
+
y: legendY,
|
|
99
|
+
parent: parentGroup,
|
|
100
|
+
})
|