websocket-text-relay 1.1.4 → 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/changelog.md +4 -0
- 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
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
const { exportDeps } = window.__WTR__
|
|
1
|
+
const { exportDeps, evalOnChange } = window.__WTR__
|
|
2
|
+
|
|
3
|
+
evalOnChange(["js/components/statusRing.js", "js/components/sessionWedges.js"])
|
|
2
4
|
|
|
3
5
|
const TWO_PI = 2 * Math.PI
|
|
4
6
|
const MAX_ANGLE_DELTA = 0.99999
|
|
@@ -27,32 +29,23 @@ const drawSvgElement = ({ tag, attributes = {}, className, parent }) => {
|
|
|
27
29
|
return element
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
const drawText = ({ x, y, text, dominantBaseline, textAnchor, className,
|
|
32
|
+
const drawText = ({ x, y, text, attributes = {}, dominantBaseline, textAnchor, className, parent }) => {
|
|
31
33
|
const textElement = drawSvgElement({
|
|
32
34
|
tag: "text",
|
|
33
|
-
attributes: { x, y
|
|
35
|
+
attributes: { ...attributes, x, y },
|
|
34
36
|
className,
|
|
35
37
|
parent,
|
|
36
38
|
})
|
|
37
39
|
textElement.textContent = text
|
|
40
|
+
if (textAnchor) {
|
|
41
|
+
textElement.style["text-anchor"] = textAnchor
|
|
42
|
+
}
|
|
43
|
+
if (dominantBaseline) {
|
|
44
|
+
textElement.style["dominant-baseline"] = dominantBaseline
|
|
45
|
+
}
|
|
38
46
|
return textElement
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
const drawLine = ({ x1, y1, x2, y2, className, parentNode: parent }) => {
|
|
42
|
-
return drawSvgElement({ tag: "line", attributes: { x1, y1, x2, y2 }, className, parent })
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const drawCircle = ({ cx, cy, r, className, parentNode: parent }) => {
|
|
46
|
-
return drawSvgElement({ tag: "circle", attributes: { cx, cy, r }, className, parent })
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const coordsToPathData = (coords) => "M " + coords.map((coord) => coord.join(",")).join(" L ")
|
|
50
|
-
|
|
51
|
-
const drawLinearPath = ({ coords, className, parentNode: parent }) => {
|
|
52
|
-
const d = coordsToPathData(coords)
|
|
53
|
-
return drawSvgElement({ tag: "path", attributes: { d }, className, parent })
|
|
54
|
-
}
|
|
55
|
-
|
|
56
49
|
const polarToCartesian = (angle, radius) => {
|
|
57
50
|
const angleRadians = (angle % 1) * TWO_PI
|
|
58
51
|
const x = Math.cos(angleRadians) * radius
|
|
@@ -60,19 +53,7 @@ const polarToCartesian = (angle, radius) => {
|
|
|
60
53
|
return [x, y]
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
const
|
|
64
|
-
const [x1, y1] = polarToCartesian(startAngle, startRadius)
|
|
65
|
-
const [x2, y2] = polarToCartesian(endAngle, endRadius)
|
|
66
|
-
|
|
67
|
-
return drawSvgElement({ tag: "line", attributes: { x1, y1, x2, y2 }, className, parent })
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const drawPolarCircle = ({ angle, radius, r, className, parentNode: parent }) => {
|
|
71
|
-
const [cx, cy] = polarToCartesian(angle, radius)
|
|
72
|
-
return drawSvgElement({ tag: "circle", attributes: { cx, cy, r }, className, parent })
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const drawWedge = ({ startAngle, angleDelta, innerRadius, radiusDelta, className, parentNode: parent }) => {
|
|
56
|
+
const drawWedge = ({ startAngle, angleDelta, innerRadius, radiusDelta, className, parent }) => {
|
|
76
57
|
if (angleDelta < 0) {
|
|
77
58
|
angleDelta = 0
|
|
78
59
|
}
|
|
@@ -89,57 +70,26 @@ const drawWedge = ({ startAngle, angleDelta, innerRadius, radiusDelta, className
|
|
|
89
70
|
const [endX2, endY2] = polarToCartesian(endAngle, outerRadius)
|
|
90
71
|
|
|
91
72
|
const d = `
|
|
92
|
-
M ${startX1}
|
|
93
|
-
A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${endX1}
|
|
94
|
-
L ${endX2}
|
|
95
|
-
A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${startX2}
|
|
73
|
+
M ${startX1},${startY1}
|
|
74
|
+
A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${endX1},${endY1}
|
|
75
|
+
L ${endX2},${endY2}
|
|
76
|
+
A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${startX2},${startY2}
|
|
96
77
|
Z
|
|
97
78
|
`
|
|
98
79
|
|
|
99
80
|
return drawSvgElement({ tag: "path", attributes: { d }, className, parent })
|
|
100
81
|
}
|
|
101
82
|
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
83
|
+
const drawPolarLine = ({ startAngle, startRadius, endAngle, endRadius, className, parent }) => {
|
|
84
|
+
const [x1, y1] = polarToCartesian(startAngle, startRadius)
|
|
85
|
+
const [x2, y2] = polarToCartesian(endAngle, endRadius)
|
|
105
86
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
})
|
|
113
|
-
const bgPlaceholder = drawSvgElement({ tag: "g", parent: tooltipDisplayGroup })
|
|
114
|
-
const textY = y + (triangleHeight + verticalPadding) * directionMultiplier
|
|
115
|
-
const textElement = drawText({
|
|
116
|
-
x,
|
|
117
|
-
y: textY,
|
|
118
|
-
text,
|
|
119
|
-
className: "tooltip_text",
|
|
120
|
-
parentNode: tooltipDisplayGroup,
|
|
121
|
-
})
|
|
122
|
-
const textBbox = textElement.getBBox()
|
|
123
|
-
const attributes = {
|
|
124
|
-
x: textBbox.x - horizontalPadding,
|
|
125
|
-
y: textBbox.y - verticalPadding,
|
|
126
|
-
width: textBbox.width + horizontalPadding * 2,
|
|
127
|
-
height: textBbox.height + verticalPadding * 2,
|
|
128
|
-
rx: 0.015,
|
|
129
|
-
}
|
|
130
|
-
drawSvgElement({ tag: "rect", attributes, className: "tooltip_outline", parent: bgPlaceholder })
|
|
87
|
+
return drawSvgElement({ tag: "line", attributes: { x1, y1, x2, y2 }, className, parent })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const drawPolarCircle = ({ angle, radius, r, className, parent }) => {
|
|
91
|
+
const [cx, cy] = polarToCartesian(angle, radius)
|
|
92
|
+
return drawSvgElement({ tag: "circle", attributes: { cx, cy, r }, className, parent })
|
|
131
93
|
}
|
|
132
94
|
|
|
133
|
-
exportDeps({
|
|
134
|
-
drawSvgElement,
|
|
135
|
-
drawLine,
|
|
136
|
-
drawCircle,
|
|
137
|
-
drawLinearPath,
|
|
138
|
-
drawPolarLine,
|
|
139
|
-
drawPolarCircle,
|
|
140
|
-
drawWedge,
|
|
141
|
-
drawText,
|
|
142
|
-
polarToCartesian,
|
|
143
|
-
coordsToPathData,
|
|
144
|
-
drawToolTip,
|
|
145
|
-
})
|
|
95
|
+
exportDeps({ drawSvgElement, drawText, polarToCartesian, drawWedge, drawPolarLine, drawPolarCircle })
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
const { exportDeps, drawSvgElement, drawCircle, drawLinearPath, coordsToPathData, drawText, drawToolTip } =
|
|
2
|
-
window.__WTR__
|
|
3
|
-
|
|
4
|
-
const drawValueWithTooltip = ({ x, y, label, direction, parentNode }) => {
|
|
5
|
-
const tooltipWrapperGroup = drawSvgElement({
|
|
6
|
-
tag: "g",
|
|
7
|
-
className: "tooltip_wrapper_group",
|
|
8
|
-
parent: parentNode,
|
|
9
|
-
})
|
|
10
|
-
drawToolTip({ x: x, y: y - 0.0032, text: label, direction, parentNode: tooltipWrapperGroup })
|
|
11
|
-
return drawText({
|
|
12
|
-
x: x,
|
|
13
|
-
y: y,
|
|
14
|
-
dominantBaseline: "middle",
|
|
15
|
-
text: "0",
|
|
16
|
-
className: "timeseries_value",
|
|
17
|
-
parentNode: tooltipWrapperGroup,
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const dataWindowSize = 16
|
|
22
|
-
const dataWindowInterval = 1000
|
|
23
|
-
const graphHeight = 0.25
|
|
24
|
-
|
|
25
|
-
const innerCircleClipPathId = "inner_circle_clip"
|
|
26
|
-
|
|
27
|
-
const createLinearScale = (domainMin, domainMax, rangeMin, rangeMax) => {
|
|
28
|
-
const domainSize = domainMax - domainMin
|
|
29
|
-
if (domainSize === 0) {
|
|
30
|
-
return () => rangeMin
|
|
31
|
-
}
|
|
32
|
-
const rangeSize = rangeMax - rangeMin
|
|
33
|
-
const ratio = rangeSize / domainSize
|
|
34
|
-
|
|
35
|
-
return (domainValue) => (domainValue - domainMin) * ratio + rangeMin
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const createRandomDataWindow = () => {
|
|
39
|
-
const series = []
|
|
40
|
-
const endTime = new Date().setMilliseconds(0).valueOf()
|
|
41
|
-
const startTime = endTime - dataWindowInterval * dataWindowSize
|
|
42
|
-
let maxValue = 0
|
|
43
|
-
|
|
44
|
-
for (let time = startTime; time < endTime; time += dataWindowInterval) {
|
|
45
|
-
// const value = Math.floor(Math.random() * 101)
|
|
46
|
-
const value = 0
|
|
47
|
-
if (value > maxValue) {
|
|
48
|
-
maxValue = value
|
|
49
|
-
}
|
|
50
|
-
series.push({ time, value })
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return series
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const getSeriesWindowInfo = (series) => {
|
|
57
|
-
const startTime = series.at(1).time
|
|
58
|
-
const endTime = series.at(-1).time
|
|
59
|
-
let maxValue = 0
|
|
60
|
-
series.forEach(({ value }) => {
|
|
61
|
-
if (value > maxValue) {
|
|
62
|
-
maxValue = value
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
return { startTime, endTime, maxValue }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
class ActivityTimeseriesGraph {
|
|
70
|
-
constructor({ innerRingRadius, parentNode }) {
|
|
71
|
-
this.innerRingRadius = innerRingRadius
|
|
72
|
-
this.parentNode = parentNode
|
|
73
|
-
this.parentNode.innerHTML = ""
|
|
74
|
-
this.dataWindow = window.activityDataWindow || createRandomDataWindow()
|
|
75
|
-
window.activityDataWindow = this.dataWindow
|
|
76
|
-
if (!window.currentActivityCount) {
|
|
77
|
-
window.currentActivityCount = 0
|
|
78
|
-
}
|
|
79
|
-
this.draw()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
draw() {
|
|
83
|
-
const minX = -this.innerRingRadius
|
|
84
|
-
const maxX = this.innerRingRadius
|
|
85
|
-
const width = maxX - minX
|
|
86
|
-
const height = graphHeight
|
|
87
|
-
const minY = -height / 2
|
|
88
|
-
const maxY = minY + height
|
|
89
|
-
|
|
90
|
-
const clipPath = drawSvgElement({
|
|
91
|
-
tag: "clipPath",
|
|
92
|
-
attributes: { id: innerCircleClipPathId },
|
|
93
|
-
parent: this.parentNode,
|
|
94
|
-
})
|
|
95
|
-
drawCircle({ cx: 0, cy: 0, r: this.innerRingRadius - 0.005, parentNode: clipPath })
|
|
96
|
-
drawSvgElement({
|
|
97
|
-
tag: "rect",
|
|
98
|
-
attributes: {
|
|
99
|
-
"clip-path": `url(#${innerCircleClipPathId})`,
|
|
100
|
-
x: minX,
|
|
101
|
-
y: minY,
|
|
102
|
-
height,
|
|
103
|
-
width,
|
|
104
|
-
},
|
|
105
|
-
className: "timeseries_bg",
|
|
106
|
-
parent: this.parentNode,
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
const series = this.dataWindow
|
|
110
|
-
const { startTime, endTime, maxValue } = getSeriesWindowInfo(series)
|
|
111
|
-
|
|
112
|
-
const maxValueElement = drawValueWithTooltip({
|
|
113
|
-
x: minX + width / 2,
|
|
114
|
-
y: minY - 0.08,
|
|
115
|
-
label: "Max updates in a 1 second window",
|
|
116
|
-
direction: "above",
|
|
117
|
-
parentNode: this.parentNode,
|
|
118
|
-
})
|
|
119
|
-
const currentValueElement = drawValueWithTooltip({
|
|
120
|
-
x: minX + width / 2,
|
|
121
|
-
y: maxY + 0.095,
|
|
122
|
-
label: "Updates in last full second",
|
|
123
|
-
parentNode: this.parentNode,
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
let valueScale = createLinearScale(0, maxValue, maxY, minY) // in svg, y increases as it goes down, so we need to flip max and min in the range
|
|
127
|
-
let timeScale = createLinearScale(startTime, endTime, minX, maxX)
|
|
128
|
-
|
|
129
|
-
const pathCoords = series.map(({ time, value }) => {
|
|
130
|
-
return [timeScale(time), valueScale(value)]
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
const graphPath = drawLinearPath({
|
|
134
|
-
coords: pathCoords,
|
|
135
|
-
className: "timeseries_path",
|
|
136
|
-
parentNode: this.parentNode,
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
if (window.activityTimeout) {
|
|
140
|
-
clearTimeout(window.activityTimeout)
|
|
141
|
-
}
|
|
142
|
-
const onTickUpdate = () => {
|
|
143
|
-
scheduleNextTick()
|
|
144
|
-
|
|
145
|
-
const series = this.dataWindow
|
|
146
|
-
const prevEndTime = series.at(-1).time
|
|
147
|
-
const newTime = prevEndTime + dataWindowInterval
|
|
148
|
-
series.shift()
|
|
149
|
-
series.push({ time: newTime, value: window.currentActivityCount })
|
|
150
|
-
currentValueElement.textContent = window.currentActivityCount
|
|
151
|
-
window.currentActivityCount = 0
|
|
152
|
-
|
|
153
|
-
const pathCoords = series.map(({ time, value }) => {
|
|
154
|
-
return [timeScale(time), valueScale(value)]
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
const pathData = coordsToPathData(pathCoords)
|
|
158
|
-
graphPath.style.transition = ""
|
|
159
|
-
graphPath.setAttribute("d", pathData)
|
|
160
|
-
|
|
161
|
-
requestAnimationFrame(() => {
|
|
162
|
-
requestAnimationFrame(() => {
|
|
163
|
-
const { startTime, endTime, maxValue } = getSeriesWindowInfo(series)
|
|
164
|
-
maxValueElement.textContent = maxValue
|
|
165
|
-
|
|
166
|
-
valueScale = createLinearScale(0, maxValue, maxY, minY) // in svg, y increases as it goes down, so we need to flip max and min in the range
|
|
167
|
-
timeScale = createLinearScale(startTime, endTime, minX, maxX)
|
|
168
|
-
|
|
169
|
-
const pathCoords = series.map(({ time, value }) => {
|
|
170
|
-
return [timeScale(time), valueScale(value)]
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
const pathData = coordsToPathData(pathCoords)
|
|
174
|
-
graphPath.style.transition = "all 1s linear"
|
|
175
|
-
graphPath.setAttribute("d", pathData)
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const scheduleNextTick = () => {
|
|
181
|
-
const nowMillis = new Date().getMilliseconds()
|
|
182
|
-
const millisUntilNextSecond = 1000 - nowMillis
|
|
183
|
-
window.activityTimeout = setTimeout(onTickUpdate, millisUntilNextSecond)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
scheduleNextTick()
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
triggerActivity() {
|
|
190
|
-
window.currentActivityCount++
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
exportDeps({ ActivityTimeseriesGraph })
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const { exportDeps, drawText } = window.__WTR__
|
|
2
|
-
|
|
3
|
-
class HeaderSummary {
|
|
4
|
-
constructor({ parentNode }) {
|
|
5
|
-
this.parentNode = parentNode
|
|
6
|
-
this.draw()
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
draw() {
|
|
10
|
-
this.parentNode.innerHTML = ""
|
|
11
|
-
drawText({ x: -0.86, y: -0.73, text: "editors", parentNode: this.parentNode })
|
|
12
|
-
drawText({
|
|
13
|
-
x: 0.86,
|
|
14
|
-
y: -0.73,
|
|
15
|
-
text: "clients",
|
|
16
|
-
className: "right_header",
|
|
17
|
-
parentNode: this.parentNode,
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
exportDeps({ HeaderSummary })
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const { exportDeps, drawText } = window.__WTR__
|
|
2
|
-
|
|
3
|
-
const valueTextClass = "server_status_value"
|
|
4
|
-
const offlineTextClass = "server_status_offline"
|
|
5
|
-
|
|
6
|
-
class ServerStatus {
|
|
7
|
-
constructor({ parentNode }) {
|
|
8
|
-
this.parentNode = parentNode
|
|
9
|
-
this.draw()
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
draw() {
|
|
13
|
-
this.parentNode.innerHTML = ""
|
|
14
|
-
drawText({
|
|
15
|
-
x: 0,
|
|
16
|
-
y: 0.85,
|
|
17
|
-
text: "WS Server PID",
|
|
18
|
-
className: "server_status_label",
|
|
19
|
-
parentNode: this.parentNode,
|
|
20
|
-
})
|
|
21
|
-
this.valueElement = drawText({ x: 0, y: 0.748, text: "138324", parentNode: this.parentNode })
|
|
22
|
-
this.offlineElement = drawText({
|
|
23
|
-
x: 0,
|
|
24
|
-
y: 0.748,
|
|
25
|
-
text: "OFFLINE",
|
|
26
|
-
className: offlineTextClass,
|
|
27
|
-
parentNode: this.parentNode,
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
update(pid) {
|
|
32
|
-
if (pid == null) {
|
|
33
|
-
this.valueElement.classList.remove(valueTextClass)
|
|
34
|
-
this.offlineElement.classList.add(offlineTextClass)
|
|
35
|
-
} else {
|
|
36
|
-
this.valueElement.textContent = pid
|
|
37
|
-
this.valueElement.classList.add(valueTextClass)
|
|
38
|
-
this.offlineElement.classList.remove(offlineTextClass)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
exportDeps({ ServerStatus })
|