@kymoscope/sdk 0.3.0 → 0.5.0
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/dist/assets/{importer.worker-Drm9u2Rc.js → importer.worker-CLBGjz4d.js} +1 -1
- package/dist/assets/{offscreenLineWorker-mShhnyXX.js → offscreenLineWorker-BLxay-Po.js} +2 -2
- package/dist/importers-Y-PAIexV.js +5 -0
- package/dist/keys-DMRJGG6U.js +10 -0
- package/dist/kymoscope-embed.js +92 -90
- package/dist/kymoscope-sdk.js +87 -84
- package/dist/rules/kyr1.schema.json +135 -0
- package/dist/rules.js +2 -0
- package/dist/styles/charts.css +191 -0
- package/dist/threshold-C9it0Y7P.js +2 -0
- package/dist/types/charts/AnnotationLayer.d.ts +7 -2
- package/dist/types/charts/CameraPanel.d.ts +43 -0
- package/dist/types/charts/ChartBody.d.ts +12 -0
- package/dist/types/charts/beacon.d.ts +4 -1
- package/dist/types/charts/cameraLayout.d.ts +10 -0
- package/dist/types/charts/liveOverlay.d.ts +14 -0
- package/dist/types/charts/runtime.d.ts +12 -1
- package/dist/types/charts/useWorksheetData.d.ts +3 -5
- package/dist/types/components/GaugeChart.d.ts +2 -0
- package/dist/types/components/MultiBandCanvasChart.d.ts +4 -2
- package/dist/types/components/dashboardGridLayout.d.ts +1 -1
- package/dist/types/components/multiBandHover.d.ts +7 -0
- package/dist/types/engine/alerts.d.ts +56 -0
- package/dist/types/engine/columns.d.ts +16 -0
- package/dist/types/engine/createEngine.d.ts +111 -3
- package/dist/types/engine/derive.d.ts +1 -1
- package/dist/types/engine/errors.d.ts +1 -1
- package/dist/types/engine/liveController.d.ts +11 -1
- package/dist/types/engine/types.d.ts +69 -4
- package/dist/types/index.d.ts +29 -4
- package/dist/types/lib/annotations.d.ts +20 -0
- package/dist/types/lib/beacon.d.ts +2 -1
- package/dist/types/lib/cycles/cut.d.ts +48 -0
- package/dist/types/lib/cycles/evaluator.d.ts +127 -0
- package/dist/types/lib/data/columnarDataSource.d.ts +1 -0
- package/dist/types/lib/data/dataSource.d.ts +3 -0
- package/dist/types/lib/data/duckDbDataSource.d.ts +1 -0
- package/dist/types/lib/data/historyDataSource.d.ts +51 -0
- package/dist/types/lib/data/inMemoryDataSource.d.ts +1 -0
- package/dist/types/lib/data/liveDataSource.d.ts +20 -1
- package/dist/types/lib/data/pointColumns.d.ts +11 -0
- package/dist/types/lib/duckdb/duckdbInstance.d.ts +5 -0
- package/dist/types/lib/historian/blockCache.d.ts +32 -0
- package/dist/types/lib/historian/export.d.ts +31 -0
- package/dist/types/lib/historian/khd1.d.ts +41 -0
- package/dist/types/lib/historian/parquet.d.ts +21 -0
- package/dist/types/lib/historian/reader.d.ts +73 -0
- package/dist/types/lib/live/blockWire.d.ts +16 -0
- package/dist/types/lib/live/buffer.d.ts +34 -0
- package/dist/types/lib/live/cameraSender.d.ts +70 -0
- package/dist/types/lib/live/clockMap.d.ts +20 -0
- package/dist/types/lib/live/crypto.d.ts +10 -0
- package/dist/types/lib/live/publisher.d.ts +168 -0
- package/dist/types/lib/live/types.d.ts +21 -3
- package/dist/types/lib/live/videoTrack.d.ts +96 -0
- package/dist/types/lib/live/videoWire.d.ts +35 -0
- package/dist/types/lib/live/wire.d.ts +4 -0
- package/dist/types/lib/panelChartSettings.d.ts +40 -1
- package/dist/types/lib/recordings/export.d.ts +1 -0
- package/dist/types/lib/recordings/videoSource.d.ts +56 -0
- package/dist/types/lib/render/alertGlyph.d.ts +7 -0
- package/dist/types/lib/render/glSeries.d.ts +1 -1
- package/dist/types/lib/segments/align.d.ts +85 -0
- package/dist/types/lib/segments/liveAxis.d.ts +65 -0
- package/dist/types/lib/segments/rules.d.ts +91 -0
- package/dist/types/lib/workspaces/io.d.ts +12 -0
- package/dist/types/lib/yAxisTicks.d.ts +11 -0
- package/dist/types/rules/compile.d.ts +47 -0
- package/dist/types/rules/evaluator.d.ts +51 -0
- package/dist/types/rules/index.d.ts +4 -0
- package/dist/types/rules/threshold.d.ts +3 -0
- package/dist/types/rules/types.d.ts +89 -0
- package/dist/types/rules/validate.d.ts +6 -0
- package/dist/types/types/loadedDataset.d.ts +20 -0
- package/dist/types/types/plotSeries.d.ts +5 -0
- package/package.json +7 -2
- package/dist/importers-DHeUdZcP.js +0 -5
- package/dist/krc-BIfw0rX1.js +0 -9
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://kymoscope.com/schemas/kyr1.schema.json",
|
|
4
|
+
"title": "Kymoscope alert rule (kyr/1)",
|
|
5
|
+
"description": "A portable alert rule. Channels are matched by exact, case-sensitive name on each target; a rule whose channels a target lacks waits there. Runtimes check the semantic limits (aliases referenced exist, derived signals are acyclic, at most 16 conditions) themselves.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": true,
|
|
8
|
+
"required": ["kyr", "id", "version", "name", "severity", "enabled", "signals", "when", "forS", "clearForS", "runWhere"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"kyr": { "const": 1 },
|
|
11
|
+
"id": { "type": "string", "minLength": 1 },
|
|
12
|
+
"version": { "type": "integer", "minimum": 1 },
|
|
13
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 120, "pattern": "\\S" },
|
|
14
|
+
"severity": { "enum": ["info", "warning", "critical"] },
|
|
15
|
+
"enabled": { "type": "boolean" },
|
|
16
|
+
"targets": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"streams": { "$ref": "#/definitions/idList" },
|
|
20
|
+
"histories": { "$ref": "#/definitions/idList" },
|
|
21
|
+
"datasets": { "$ref": "#/definitions/idList" }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"signals": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"minProperties": 1,
|
|
27
|
+
"propertyNames": { "$ref": "#/definitions/alias" },
|
|
28
|
+
"additionalProperties": { "$ref": "#/definitions/signal" }
|
|
29
|
+
},
|
|
30
|
+
"derived": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"propertyNames": { "$ref": "#/definitions/alias" },
|
|
33
|
+
"additionalProperties": { "$ref": "#/definitions/derived" }
|
|
34
|
+
},
|
|
35
|
+
"when": { "$ref": "#/definitions/condition" },
|
|
36
|
+
"forS": { "$ref": "#/definitions/seconds" },
|
|
37
|
+
"clearForS": { "$ref": "#/definitions/seconds" },
|
|
38
|
+
"staleS": { "oneOf": [{ "$ref": "#/definitions/seconds" }, { "type": "null" }] },
|
|
39
|
+
"notify": { "type": "array", "items": { "type": "string" } },
|
|
40
|
+
"runWhere": { "type": "array", "minItems": 1, "items": { "enum": ["browser", "collector", "watcher", "cloud"] } },
|
|
41
|
+
"keep": {
|
|
42
|
+
"oneOf": [
|
|
43
|
+
{ "type": "null" },
|
|
44
|
+
{ "type": "object", "required": ["beforeS", "afterS"], "properties": { "beforeS": { "$ref": "#/definitions/seconds" }, "afterS": { "$ref": "#/definitions/seconds" } } }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"origin": {
|
|
48
|
+
"oneOf": [
|
|
49
|
+
{ "type": "null" },
|
|
50
|
+
{
|
|
51
|
+
"type": "object",
|
|
52
|
+
"required": ["workbookId", "worksheetId", "chartId", "field"],
|
|
53
|
+
"properties": {
|
|
54
|
+
"workbookId": { "type": ["string", "null"] },
|
|
55
|
+
"worksheetId": { "type": "string" },
|
|
56
|
+
"chartId": { "type": "string" },
|
|
57
|
+
"field": { "type": "string" },
|
|
58
|
+
"seriesId": { "type": "string" }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"definitions": {
|
|
65
|
+
"alias": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,31}$" },
|
|
66
|
+
"channel": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
67
|
+
"seconds": { "type": "number", "minimum": 0, "maximum": 86400 },
|
|
68
|
+
"hysteresis": { "type": "number", "minimum": 0 },
|
|
69
|
+
"idList": { "oneOf": [{ "type": "null" }, { "type": "array", "items": { "type": "string" } }] },
|
|
70
|
+
"signal": {
|
|
71
|
+
"oneOf": [
|
|
72
|
+
{
|
|
73
|
+
"type": "object",
|
|
74
|
+
"required": ["channel"],
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"properties": {
|
|
77
|
+
"channel": { "$ref": "#/definitions/channel" },
|
|
78
|
+
"stream": { "type": "string", "description": "Only on this stream id." },
|
|
79
|
+
"history": { "type": "string", "description": "Only on this history id." },
|
|
80
|
+
"dataset": { "type": "string", "description": "Only on loaded files with this name." },
|
|
81
|
+
"tag": { "type": "string", "description": "Collector only: bind by the source address (OPC UA node id) instead of the published name." }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"type": "object",
|
|
86
|
+
"required": ["formula"],
|
|
87
|
+
"additionalProperties": false,
|
|
88
|
+
"description": "A math channel by name. The rule's runWhere must be exactly [\"browser\"].",
|
|
89
|
+
"properties": { "formula": { "$ref": "#/definitions/channel" } }
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"derived": {
|
|
94
|
+
"oneOf": [
|
|
95
|
+
{
|
|
96
|
+
"type": "object", "required": ["op", "of", "windowS"], "additionalProperties": false,
|
|
97
|
+
"properties": { "op": { "enum": ["rate", "mean", "min", "max"] }, "of": { "$ref": "#/definitions/alias" }, "windowS": { "type": "number", "exclusiveMinimum": 0, "maximum": 86400 } }
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"type": "object", "required": ["op", "of"], "additionalProperties": false,
|
|
101
|
+
"properties": { "op": { "const": "abs" }, "of": { "$ref": "#/definitions/alias" } }
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"type": "object", "required": ["op", "a", "b"], "additionalProperties": false,
|
|
105
|
+
"properties": { "op": { "const": "diff" }, "a": { "$ref": "#/definitions/alias" }, "b": { "$ref": "#/definitions/alias" } }
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"condition": {
|
|
110
|
+
"description": "Nesting depth at most 3, at most 16 leaves. Other condition keys are refused (unsupported_condition).",
|
|
111
|
+
"oneOf": [
|
|
112
|
+
{ "type": "object", "required": ["above", "value"], "additionalProperties": false,
|
|
113
|
+
"properties": { "above": { "$ref": "#/definitions/alias" }, "value": { "type": "number" }, "hysteresis": { "$ref": "#/definitions/hysteresis" } } },
|
|
114
|
+
{ "type": "object", "required": ["below", "value"], "additionalProperties": false,
|
|
115
|
+
"properties": { "below": { "$ref": "#/definitions/alias" }, "value": { "type": "number" }, "hysteresis": { "$ref": "#/definitions/hysteresis" } } },
|
|
116
|
+
{ "type": "object", "required": ["outside", "lo", "hi"], "additionalProperties": false,
|
|
117
|
+
"properties": { "outside": { "$ref": "#/definitions/alias" }, "lo": { "type": "number" }, "hi": { "type": "number" }, "hysteresis": { "$ref": "#/definitions/hysteresis" } } },
|
|
118
|
+
{ "type": "object", "required": ["inside", "lo", "hi"], "additionalProperties": false,
|
|
119
|
+
"properties": { "inside": { "$ref": "#/definitions/alias" }, "lo": { "type": "number" }, "hi": { "type": "number" }, "hysteresis": { "$ref": "#/definitions/hysteresis" } } },
|
|
120
|
+
{ "type": "object", "required": ["equals", "value"], "additionalProperties": false,
|
|
121
|
+
"properties": { "equals": { "$ref": "#/definitions/alias" }, "value": { "type": "number" } } },
|
|
122
|
+
{ "type": "object", "required": ["changes"], "additionalProperties": false,
|
|
123
|
+
"properties": { "changes": { "$ref": "#/definitions/alias" } } },
|
|
124
|
+
{ "type": "object", "required": ["noData", "forS"], "additionalProperties": false,
|
|
125
|
+
"properties": { "noData": { "$ref": "#/definitions/alias" }, "forS": { "$ref": "#/definitions/seconds" } } },
|
|
126
|
+
{ "type": "object", "required": ["all"], "additionalProperties": false,
|
|
127
|
+
"properties": { "all": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/condition" } } } },
|
|
128
|
+
{ "type": "object", "required": ["any"], "additionalProperties": false,
|
|
129
|
+
"properties": { "any": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/condition" } } } },
|
|
130
|
+
{ "type": "object", "required": ["not"], "additionalProperties": false,
|
|
131
|
+
"properties": { "not": { "$ref": "#/definitions/condition" } } }
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
package/dist/rules.js
ADDED
package/dist/styles/charts.css
CHANGED
|
@@ -112,6 +112,9 @@
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
.kymo-ann { cursor: move; }
|
|
115
|
+
/* Alert marks: read-only spans; only their top strip takes the pointer (it selects the rule). */
|
|
116
|
+
.kymo-marks { pointer-events: none; }
|
|
117
|
+
.kymo-mark-hit { fill: transparent; pointer-events: auto; cursor: pointer; }
|
|
115
118
|
.kymo-ann-hit {
|
|
116
119
|
fill: none;
|
|
117
120
|
stroke: transparent;
|
|
@@ -205,3 +208,191 @@
|
|
|
205
208
|
@media (prefers-reduced-motion: no-preference) {
|
|
206
209
|
.kymo-ann-delete { transition: color 0.14s ease, border-color 0.14s ease; }
|
|
207
210
|
}
|
|
211
|
+
|
|
212
|
+
/* ── Camera panel ──────────────────────────────────────────────────────────
|
|
213
|
+
The video itself stays plain: no glow, no motion. The overlay is a small
|
|
214
|
+
readout over it; only the LIVE dot breathes (not with reduced motion). */
|
|
215
|
+
.kymo-camera {
|
|
216
|
+
position: relative;
|
|
217
|
+
width: 100%;
|
|
218
|
+
height: 100%;
|
|
219
|
+
overflow: hidden;
|
|
220
|
+
background: var(--chart-bg);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.kymo-camera__video {
|
|
224
|
+
position: absolute;
|
|
225
|
+
inset: 0;
|
|
226
|
+
display: block;
|
|
227
|
+
width: 100%;
|
|
228
|
+
height: 100%;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.kymo-camera__overlay {
|
|
232
|
+
position: absolute;
|
|
233
|
+
top: 8px;
|
|
234
|
+
left: 8px;
|
|
235
|
+
display: flex;
|
|
236
|
+
flex-wrap: wrap;
|
|
237
|
+
align-items: center;
|
|
238
|
+
gap: 4px 8px;
|
|
239
|
+
max-width: calc(100% - 16px);
|
|
240
|
+
padding: 3px 8px;
|
|
241
|
+
border-radius: 6px;
|
|
242
|
+
font: 500 11px/1.5 var(--font-mono, ui-monospace, monospace);
|
|
243
|
+
font-variant-numeric: tabular-nums;
|
|
244
|
+
font-feature-settings: 'tnum' 1;
|
|
245
|
+
color: var(--chart-fg, oklch(0.93 0.01 255));
|
|
246
|
+
background: color-mix(in oklch, var(--chart-bg) 80%, transparent);
|
|
247
|
+
box-shadow:
|
|
248
|
+
inset 0 1px 0 color-mix(in oklch, white 7%, transparent),
|
|
249
|
+
0 0 0 1px color-mix(in oklch, var(--chart-grid) 70%, transparent),
|
|
250
|
+
0 2px 6px color-mix(in oklch, black 28%, transparent);
|
|
251
|
+
pointer-events: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
[data-color-scheme='light'] .kymo-camera__overlay { color: oklch(0.25 0.03 255); }
|
|
255
|
+
|
|
256
|
+
.kymo-camera__name {
|
|
257
|
+
overflow: hidden;
|
|
258
|
+
max-width: 24ch;
|
|
259
|
+
text-overflow: ellipsis;
|
|
260
|
+
white-space: nowrap;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.kymo-camera__live {
|
|
264
|
+
display: inline-flex;
|
|
265
|
+
align-items: center;
|
|
266
|
+
gap: 5px;
|
|
267
|
+
font-weight: 700;
|
|
268
|
+
letter-spacing: 0.04em;
|
|
269
|
+
color: var(--accent);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.kymo-camera__live::before {
|
|
273
|
+
content: '';
|
|
274
|
+
width: 6px;
|
|
275
|
+
height: 6px;
|
|
276
|
+
border-radius: 50%;
|
|
277
|
+
background: currentColor;
|
|
278
|
+
box-shadow: 0 0 0 2px color-mix(in oklch, var(--accent) 22%, transparent);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.kymo-camera__ago { color: var(--chart-muted); }
|
|
282
|
+
|
|
283
|
+
.kymo-camera__rec {
|
|
284
|
+
font-weight: 700;
|
|
285
|
+
letter-spacing: 0.04em;
|
|
286
|
+
color: var(--chart-muted);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.kymo-camera__badge {
|
|
290
|
+
padding: 0 5px;
|
|
291
|
+
border-radius: 4px;
|
|
292
|
+
border: 1px solid var(--chart-grid);
|
|
293
|
+
color: var(--chart-muted);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.kymo-camera__badge.is-exact {
|
|
297
|
+
border-color: color-mix(in oklch, var(--accent) 45%, transparent);
|
|
298
|
+
color: var(--accent);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.kymo-camera__note { color: var(--accent); }
|
|
302
|
+
|
|
303
|
+
.kymo-camera__empty {
|
|
304
|
+
position: absolute;
|
|
305
|
+
inset: 0;
|
|
306
|
+
display: grid;
|
|
307
|
+
place-content: center;
|
|
308
|
+
justify-items: center;
|
|
309
|
+
gap: 6px;
|
|
310
|
+
padding: 16px;
|
|
311
|
+
text-align: center;
|
|
312
|
+
color: var(--chart-muted);
|
|
313
|
+
font: 13px/1.45 var(--font-sans, system-ui, sans-serif);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.kymo-camera__empty p { margin: 0; }
|
|
317
|
+
|
|
318
|
+
.kymo-camera__empty-icon {
|
|
319
|
+
width: 30px;
|
|
320
|
+
height: 30px;
|
|
321
|
+
margin-bottom: 2px;
|
|
322
|
+
fill: none;
|
|
323
|
+
stroke: currentColor;
|
|
324
|
+
stroke-width: 1.5;
|
|
325
|
+
stroke-linejoin: round;
|
|
326
|
+
opacity: 0.75;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.kymo-camera__empty-title {
|
|
330
|
+
font-weight: 600;
|
|
331
|
+
color: var(--chart-fg, oklch(0.93 0.01 255));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
[data-color-scheme='light'] .kymo-camera__empty-title { color: oklch(0.25 0.03 255); }
|
|
335
|
+
|
|
336
|
+
.kymo-camera__empty-detail { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }
|
|
337
|
+
|
|
338
|
+
/* A wall of cameras: tiles on a hairline grid (the gap shows the chart grid colour). */
|
|
339
|
+
.kymo-camera-wall {
|
|
340
|
+
position: relative;
|
|
341
|
+
display: grid;
|
|
342
|
+
gap: 2px;
|
|
343
|
+
width: 100%;
|
|
344
|
+
height: 100%;
|
|
345
|
+
overflow: hidden;
|
|
346
|
+
background: var(--chart-grid);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.kymo-camera.is-tile { cursor: zoom-in; }
|
|
350
|
+
|
|
351
|
+
/* Hover and keyboard focus ring the tile itself; the video is never dimmed or animated. */
|
|
352
|
+
.kymo-camera.is-tile::after {
|
|
353
|
+
content: '';
|
|
354
|
+
position: absolute;
|
|
355
|
+
inset: 0;
|
|
356
|
+
box-shadow: inset 0 0 0 1px transparent;
|
|
357
|
+
pointer-events: none;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.kymo-camera.is-tile:hover::after { box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--accent) 55%, transparent); }
|
|
361
|
+
.kymo-camera.is-tile:focus-visible { outline: none; }
|
|
362
|
+
.kymo-camera.is-tile:focus-visible::after { box-shadow: inset 0 0 0 2px var(--accent); }
|
|
363
|
+
|
|
364
|
+
.kymo-camera__all {
|
|
365
|
+
position: absolute;
|
|
366
|
+
top: 8px;
|
|
367
|
+
right: 8px;
|
|
368
|
+
display: inline-flex;
|
|
369
|
+
align-items: center;
|
|
370
|
+
gap: 6px;
|
|
371
|
+
padding: 4px 10px 4px 8px;
|
|
372
|
+
border: 0;
|
|
373
|
+
border-radius: 6px;
|
|
374
|
+
font: 500 12px/1.4 var(--font-sans, system-ui, sans-serif);
|
|
375
|
+
color: var(--chart-fg, oklch(0.93 0.01 255));
|
|
376
|
+
background: color-mix(in oklch, var(--chart-bg) 82%, transparent);
|
|
377
|
+
box-shadow:
|
|
378
|
+
inset 0 1px 0 color-mix(in oklch, white 7%, transparent),
|
|
379
|
+
0 0 0 1px color-mix(in oklch, var(--chart-grid) 70%, transparent),
|
|
380
|
+
0 2px 6px color-mix(in oklch, black 28%, transparent);
|
|
381
|
+
cursor: pointer;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.kymo-camera__all svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.4; }
|
|
385
|
+
.kymo-camera__all:hover { box-shadow: inset 0 1px 0 color-mix(in oklch, white 7%, transparent), 0 0 0 1px color-mix(in oklch, var(--accent) 55%, transparent), 0 2px 6px color-mix(in oklch, black 28%, transparent); }
|
|
386
|
+
.kymo-camera__all:hover svg { color: var(--accent); }
|
|
387
|
+
.kymo-camera__all:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
388
|
+
|
|
389
|
+
[data-color-scheme='light'] .kymo-camera__all { color: oklch(0.25 0.03 255); }
|
|
390
|
+
|
|
391
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
392
|
+
.kymo-camera__live::before { animation: kymo-camera-live 2s ease-in-out infinite; }
|
|
393
|
+
.kymo-camera.is-tile::after, .kymo-camera__all { transition: box-shadow 0.15s ease; }
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@keyframes kymo-camera-live {
|
|
397
|
+
50% { box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent) 8%, transparent); }
|
|
398
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! @kymoscope/sdk (c) 2026 Kymoscope. Proprietary software: use requires a license. See LICENSE.md. */
|
|
2
|
+
function e(e){let n=Object.keys(e.signals),r=t(e.derived??{}),i=[...n,...r],a=e=>i.indexOf(e),o=r.map(t=>{let n=e.derived[t];return{i:a(t),d:n,src:(n.op===`diff`?[n.a,n.b]:[n.of]).map(a)}}),s=[],c=i.map(()=>!1),l=i.map(()=>e.staleS!=null),u=null,d=!1,f=e=>{d||(d=!0,u=e)},p=e=>{let t=e;if(`all`in t||`any`in t){let e=`all`in t,n=(e?t.all:t.any).map(p),r=s.length,i=n.filter(e=>`i`in s[e]);i.forEach((t,n)=>{let i=s[t];i.into=r,i.mode=n===0?0:e?1:2}),s.push({k:e?`all`:`any`,kids:n,rest:n.filter(e=>!i.includes(e)),fused:i.length>0})}else if(`not`in t){let e=p(t.not);s.push({k:`not`,kid:e})}else{let e=[`above`,`below`,`outside`,`inside`,`equals`,`changes`,`noData`].find(e=>e in t),n=a(t[e]),r=t.value??NaN,i=t.lo??NaN,o=t.hi??NaN;f(e===`above`?{i:n,excess:e=>e-r}:e===`below`?{i:n,excess:e=>r-e}:e===`outside`?{i:n,excess:e=>Math.max(e-o,i-e)}:null),e===`changes`&&(c[n]=!0),e===`noData`&&(l[n]=!0),s.push({k:e,i:n,v:r,lo:i,hi:o,h:t.hysteresis??0,f:t.forS??0,prev:0,into:s.length,mode:0})}return s.length-1};return p(e.when),{aliases:i,base:n.length,derived:o,ops:s,needPrev:c,needAt:l,first:u}}function t(e){let t=[],n=new Set,r=i=>{if(n.has(i)||!(i in e))return;n.add(i);let a=e[i];for(let e of a.op===`diff`?[a.a,a.b]:[a.of])r(e);t.push(i)};return Object.keys(e).forEach(r),t}var n=`\0t`;function r(e,t){return`formula`in e?{applies:!0,column:e.formula}:e.stream&&!(t.kind===`stream`&&t.id===e.stream)||e.history&&!(t.kind===`history`&&t.id===e.history)||e.dataset&&!(t.kind===`dataset`&&t.name===e.dataset)?{applies:!1,column:null}:e.tag?{applies:!0,column:t.tags?.[e.tag]??null}:{applies:!0,column:e.channel}}function i(t,n){let i=e(t),f=i.aliases.length,p=i.base,m=t.staleS??1/0,h=Object.values(t.signals),g=h.every(e=>r(e,n).applies),_=h.map(()=>null),v=!1,y=[],b=`idle`,x=NaN,S=NaN,C=-1/0,w=0,T=null,E=new Float64Array(f).fill(NaN),D=new Float64Array(f).fill(NaN),O=new Float64Array(f).fill(NaN),k=i.derived.map(()=>l()),A=Array(f),j=Array(f),M=Array(f),N=Array(f),P=0,F=[],I=[],L=[],R=[],z=new Float64Array,B=new Float64Array,V=new Float64Array,H=new Int32Array,U=new Uint8Array,W=[],G=e=>{e<=P||(P=Math.max(e,P*2,64),F=Array.from({length:f},()=>new Float64Array(P)),I=Array.from({length:f},()=>new Float64Array(P)),L=Array.from({length:f},()=>new Uint8Array(P)),R=Array.from({length:f},()=>new Float64Array(P)),z=new Float64Array(P),B=new Float64Array(P),V=new Float64Array(P),H=new Int32Array(P),U=new Uint8Array(P).fill(1),W=i.ops.map(()=>new Int8Array(P)))},K=(e,r,i={})=>({ruleId:t.id,ruleVersion:t.version,target:n,kind:e,t:r,...i}),q=()=>{E.fill(NaN),D.fill(NaN),O.fill(NaN);for(let e of k)e.head=0,e.len=0,e.sum=0};function J(e){let i=[];if(!t.enabled||!g)return i;let a=new Set(e),o=h.map(e=>r(e,n).column),s=h.map((e,t)=>o[t]!==null&&a.has(o[t])?null:`formula`in e?e.formula:e.channel).filter(e=>e!==null),c=v;return _=o,y=[...new Set(s)],v=y.length===0,c&&!v&&((b===`active`||b===`clearing`)&&i.push(K(`lost`,C)),b=`idle`,T=null,q()),i}function Y(e,t){for(let n=0;n<i.derived.length;n++){let{i:r,d:a,src:o}=i.derived[n],s=A[o[0]],c=M[o[0]],l=F[r],f=I[r],p=L[r],m=R[r],h=i.needPrev[r],g=E[r],_=D[r],v=O[r];if(a.op===`abs`||a.op===`diff`){let n=a.op===`diff`,i=n?A[o[1]]:s,u=n?M[o[1]]:c;if((c===U||u===U)&&!h){if(n)for(let t=0;t<e;t++)l[t]=s[t]-i[t];else for(let t=0;t<e;t++)l[t]=Math.abs(s[t]);O[r]=e>=2?l[e-2]:E[r],E[r]=l[e-1],D[r]=t[e-1],A[r]=l,j[r]=t,M[r]=U,N[r]=m;continue}for(let r=0;r<e;r++)c[r]||n&&u[r]?(v=g,g=n?s[r]-i[r]:Math.abs(s[r]),_=t[r],p[r]=1):p[r]=0,l[r]=g,f[r]=_,h&&(m[r]=v)}else{let o=k[n],y=a.windowS;if(a.op===`min`||a.op===`max`){let n=a.op===`max`;for(let r=0;r<e;r++){let e=t[r],i=e-y,a=c[r]===1,u=s[r];if(a){for(;o.len&&(n?o.x[o.head+o.len-1&o.mask]<=u:o.x[o.head+o.len-1&o.mask]>=u);)o.len--;d(o,e,u)}for(;o.len&&o.t[o.head]<i;)o.head=o.head+1&o.mask,o.len--;if(a){let t=o.x[o.head];v=g,g=t,t===t&&(_=e),p[r]=1}else p[r]=0;l[r]=g,f[r]=_,h&&(m[r]=v)}}else{let n=a.op===`mean`,d=o.len;z.length<d+e&&(z=new Float64Array(2*(d+e)),B=new Float64Array(2*(d+e)));for(let e=0;e<d;e++){let t=o.head+e&o.mask;z[e]=o.t[t],B[e]=o.x[t]}let b=0,x=d,S=o.sum;if(c===U&&!h&&!i.needAt[r]){for(let r=0;r<e;r++){let e=t[r],i=s[r],a=e-y;if(z[x]=e,B[x]=i,x++,n)for(S+=i;z[b]<a;)S-=B[b],b++;else for(;z[b]<a;)b++;l[r]=g=n?S/(x-b):x-b>=2&&e>z[b]?(i-B[b])/(e-z[b]):NaN}v=e>=2?l[e-2]:E[r];for(let n=e-1;n>=0;n--)if(l[n]===l[n]){_=t[n];break}o.head=0,o.len=0,u(o,z,B,b,x),o.sum=S,E[r]=g,D[r]=_,O[r]=v,A[r]=l,j[r]=t,M[r]=U,N[r]=m;continue}for(let r=0;r<e;r++){if(c[r]===1){let e=t[r],i=s[r],a=e-y;if(z[x]=e,B[x]=i,x++,n)for(S+=i;z[b]<a;)S-=B[b],b++;else for(;z[b]<a;)b++;let o=n?S/(x-b):x-b>=2&&e>z[b]?(i-B[b])/(e-z[b]):NaN;v=g,g=o,o===o&&(_=e),p[r]=1}else p[r]=0;l[r]=g,f[r]=_,h&&(m[r]=v)}o.head=0,o.len=0,u(o,z,B,b,x),o.sum=S}}E[r]=g,D[r]=_,O[r]=v,A[r]=l,j[r]=f,M[r]=p,N[r]=m}}function X(e,t){let n=m!==1/0;for(let r=0;r<i.ops.length;r++){let a=i.ops[r];if(a.k===`all`||a.k===`any`){let t=W[r],n=a.k===`all`,i=a.rest,o=0;for(a.fused||(t.set(W[i[0]].subarray(0,e)),o=1);o<i.length;o++){let r=W[i[o]];if(n)for(let n=0;n<e;n++)r[n]<t[n]&&(t[n]=r[n]);else for(let n=0;n<e;n++)r[n]>t[n]&&(t[n]=r[n])}continue}if(a.k===`not`){let t=W[r],n=W[a.kid];for(let r=0;r<e;r++)t[r]=c-n[r];continue}let o=a,l=A[o.i],u=j[o.i],d=W[o.into],f=o.mode,p=o.prev;switch(a.k){case`noData`:{let n=a.f;for(let r=0;r<e;r++){let e=u[r]===u[r]?t[r]-u[r]>=n?c:0:s;(f===0||(f===2?e>d[r]:e<d[r]))&&(d[r]=e)}break}case`above`:{let r=a.v,i=r-a.h;for(let a=0;a<e;a++){let e=l[a],o;e!==e||n&&t[a]-u[a]>=m&&u[a]!==t[a]?o=s:(e>r?p=c:e<=i&&(p=0),o=p),(f===0||(f===2?o>d[a]:o<d[a]))&&(d[a]=o)}break}case`below`:{let r=a.v,i=r+a.h;for(let a=0;a<e;a++){let e=l[a],o;e!==e||n&&t[a]-u[a]>=m&&u[a]!==t[a]?o=s:(e<r?p=c:e>=i&&(p=0),o=p),(f===0||(f===2?o>d[a]:o<d[a]))&&(d[a]=o)}break}case`outside`:{let r=a.lo,i=a.hi,o=r+a.h,h=i-a.h;for(let a=0;a<e;a++){let e=l[a],g;e!==e||n&&t[a]-u[a]>=m&&u[a]!==t[a]?g=s:(e<r||e>i?p=c:e>=o&&e<=h&&(p=0),g=p),(f===0||(f===2?g>d[a]:g<d[a]))&&(d[a]=g)}break}case`inside`:{let r=a.lo,i=a.hi,o=r-a.h,h=i+a.h;for(let a=0;a<e;a++){let e=l[a],g;e!==e||n&&t[a]-u[a]>=m&&u[a]!==t[a]?g=s:(e>=r&&e<=i?p=c:(e<o||e>h)&&(p=0),g=p),(f===0||(f===2?g>d[a]:g<d[a]))&&(d[a]=g)}break}case`equals`:{let r=a.v;for(let i=0;i<e;i++){let e=l[i],a;a=e!==e||n&&t[i]-u[i]>=m&&u[i]!==t[i]?s:e===r?c:0,(f===0||(f===2?a>d[i]:a<d[i]))&&(d[i]=a)}break}case`changes`:{let r=M[a.i],i=N[a.i];for(let a=0;a<e;a++){let e=l[a],o;if(e!==e||n&&t[a]-u[a]>=m&&u[a]!==t[a])o=s;else{let t=i[a];o=t===t?r[a]&&e!==t?c:0:s}(f===0||(f===2?o>d[a]:o<d[a]))&&(d[a]=o)}break}}a.prev=p}return W[i.ops.length-1]}function Z(e,n,r,a){let o=i.first,l=o?A[o.i]:null;for(let i=0;i<e;i++){let e=r[i];if(b===`idle`){if(e!==c)continue;b=`pending`,x=n[i],T=null}if(l){let e=l[i];if(e===e){let t=o.excess(e);t>0&&(T===null||t>o.excess(T.v))&&(T={v:e,t:n[i]})}}if(e===s)continue;let u=n[i];if(b===`pending`){if(e===0){b=`idle`,T=null;continue}u-x>=t.forS&&(b=`active`,a.push(K(`raised`,x)));continue}if(b===`active`){if(e===c)continue;b=`clearing`,S=u}else if(e===c){b=`active`;continue}u-S>=t.clearForS&&(b=`idle`,a.push(K(`cleared`,S,T?{peak:T}:{})),T=null)}}function Q(e){let n=[];if(!t.enabled||!g||!v)return n;let r=e.t,s=r.length;if(!s)return n;G(s);let c=null,l=_.map(t=>t!==null&&e.columns[t]||(c??=new Float64Array(s).fill(NaN))),u=l.map((t,n)=>o(e,_[n],t)),d=r[0]>C&&a(e)&&u.some(Boolean),f,m;if(d)f=s,m=r,C=r[s-1];else{f=0;for(let e=0;e<s;e++){let t=!1;for(let n=0;n<p;n++)if(l[n][e]===l[n][e]){t=!0;break}if(!t)continue;let n=r[e];if(n<=C){w++;continue}H[f]=e,V[f]=n,f++,C=n}if(!f)return n;m=V}for(let e=0;e<p;e++){let t=l[e],n=i.needPrev[e];if(d&&u[e]){if(n){let n=R[e];n[0]=E[e];for(let e=1;e<f;e++)n[e]=t[e-1];N[e]=n}O[e]=f>=2?t[f-2]:E[e],E[e]=t[f-1],D[e]=r[f-1],A[e]=t,j[e]=r,M[e]=U;continue}let a=F[e],o=I[e],s=L[e],c=R[e],p=E[e],h=D[e],g=O[e];for(let e=0;e<f;e++){let r=t[d?e:H[e]];r===r?(g=p,p=r,h=m[e],s[e]=1):s[e]=0,a[e]=p,o[e]=h,n&&(c[e]=g)}E[e]=p,D[e]=h,O[e]=g,A[e]=a,j[e]=o,M[e]=s,N[e]=c}return Y(f,m),Z(f,m,X(f,m),n),n}function $(e){let n=[];if(!t.enabled||!g||!v||e<=C)return n;G(1);for(let e=0;e<f;e++)F[e][0]=E[e],I[e][0]=D[e],L[e][0]=0,R[e][0]=O[e],A[e]=F[e],j[e]=I[e],M[e]=L[e],N[e]=R[e];return V[0]=e,Z(1,V,X(1,V),n),n}function ee(){let e=t.enabled?g?v?b:`waiting`:`not_applicable`:`disabled`;return{state:e,...e===`waiting`?{missing:y}:{},...b!==`idle`&&v?{since:b===`clearing`?S:x}:{},late:w,lastT:C}}return J([]),{rule:t,target:n,bind:J,push:Q,tick:$,state:ee}}function a(e){let t=e.scan?.get(n);if(t===void 0){let r=e.t;t=!0;for(let e=1;e<r.length;e++)if(!(r[e]>r[e-1])){t=!1;break}e.scan?.set(n,t)}return t}function o(e,t,n){let r=t===null?void 0:e.scan?.get(t);if(r===void 0){r=!0;for(let e=0;e<n.length;e++)if(n[e]!==n[e]){r=!1;break}t!==null&&e.scan?.set(t,r)}return r}var s=1,c=2,l=()=>({t:new Float64Array(16),x:new Float64Array(16),head:0,len:0,mask:15,sum:0});function u(e,t,n,r,i){let a=i-r;if(a<=0)return;if(e.len+a>e.mask+1){let t=e.mask+1;for(;t<e.len+a;)t*=2;let n=new Float64Array(t),r=new Float64Array(t);for(let t=0;t<e.len;t++)n[t]=e.t[e.head+t&e.mask],r[t]=e.x[e.head+t&e.mask];e.t=n,e.x=r,e.head=0,e.mask=t-1}let o=e.head+e.len&e.mask,s=Math.min(a,e.mask+1-o);e.t.set(t.subarray(r,r+s),o),e.x.set(n.subarray(r,r+s),o),s<a&&(e.t.set(t.subarray(r+s,i),0),e.x.set(n.subarray(r+s,i),0)),e.len+=a}function d(e,t,n){if(e.len===e.mask+1){let t=e.len*2,n=new Float64Array(t),r=new Float64Array(t);for(let t=0;t<e.len;t++)n[t]=e.t[e.head+t&e.mask],r[t]=e.x[e.head+t&e.mask];e.t=n,e.x=r,e.head=0,e.mask=t-1}let r=e.head+e.len&e.mask;e.t[r]=t,e.x[r]=n,e.len++}var f=[`above`,`below`,`outside`,`inside`,`equals`,`changes`,`noData`],p=/^[A-Za-z_][A-Za-z0-9_]{0,31}$/,m=[`info`,`warning`,`critical`],h=[`browser`,`collector`,`watcher`,`cloud`],g=e=>typeof e==`number`&&Number.isFinite(e),_=e=>g(e)&&e>=0&&e<=86400;function v(e){let t=[],n=(e,n)=>{t.push({code:e,path:n})};if(!e||typeof e!=`object`)return[{code:`not_a_rule`,path:``}];let r=e;r.kyr!==1&&n(`unsupported_version`,`kyr`),(typeof r.id!=`string`||!r.id)&&n(`bad_id`,`id`),(!Number.isInteger(r.version)||r.version<1)&&n(`bad_version`,`version`),(typeof r.name!=`string`||!r.name.trim()||r.name.length>120)&&n(`bad_name`,`name`),m.includes(r.severity)||n(`bad_severity`,`severity`);for(let e of[`forS`,`clearForS`])_(r[e])||n(`bad_duration`,e);r.staleS!=null&&!_(r.staleS)&&n(`bad_duration`,`staleS`),(!Array.isArray(r.runWhere)||!r.runWhere.length||!r.runWhere.every(e=>h.includes(e)))&&n(`bad_run_where`,`runWhere`);let i=new Map,a=r.signals&&typeof r.signals==`object`?r.signals:{};Object.keys(a).length||n(`no_signals`,`signals`);for(let[e,t]of Object.entries(a)){p.test(e)||n(`bad_alias`,`signals.${e}`),i.set(e,`signal`);let a=t&&typeof t==`object`?t:{};`formula`in a?((typeof a.formula!=`string`||!a.formula)&&n(`bad_channel`,`signals.${e}`),Array.isArray(r.runWhere)&&r.runWhere.length===1&&r.runWhere[0]===`browser`||n(`formula_browser_only`,`signals.${e}`)):(typeof a.channel!=`string`||!a.channel||a.channel.length>200)&&n(`bad_channel`,`signals.${e}`)}let o=r.derived&&typeof r.derived==`object`?r.derived:{};for(let e of Object.keys(o))p.test(e)||n(`bad_alias`,`derived.${e}`),i.has(e)&&n(`duplicate_alias`,`derived.${e}`),i.set(e,`derived`);let s=e=>e.op===`diff`?[e.a,e.b]:[e.of],c=new Set;for(let[e,t]of Object.entries(o)){let r=t&&typeof t==`object`?t:{};if(![`rate`,`mean`,`min`,`max`,`abs`,`diff`].includes(r.op)){n(`bad_derived`,`derived.${e}`);continue}c.add(e),[`rate`,`mean`,`min`,`max`].includes(r.op)&&!(_(r.windowS)&&r.windowS>0)&&n(`bad_duration`,`derived.${e}.windowS`);for(let t of s(r))(typeof t!=`string`||!i.has(t))&&n(`unknown_alias`,`derived.${e}`)}let l=new Set,u=new Set,d=e=>{if(u.has(e)||i.get(e)!==`derived`||!c.has(e))return!0;if(l.has(e))return!1;l.add(e);let t=s(o[e]).every(e=>typeof e!=`string`||d(e));return l.delete(e),u.add(e),t};for(let e of Object.keys(o))if(!d(e)){n(`derived_cycle`,`derived.${e}`);break}let v=0,y=(e,t,r)=>{if(!e||typeof e!=`object`)return n(`bad_condition`,t);let a=e;for(let e of[`all`,`any`])if(e in a)return r>=3?n(`too_deep`,t):!Array.isArray(a[e])||!a[e].length?n(`bad_condition`,t):a[e].forEach((n,i)=>y(n,`${t}.${e}[${i}]`,r+1));if(`not`in a)return r>=3?n(`too_deep`,t):y(a.not,`${t}.not`,r+1);let o=f.find(e=>e in a);if(!o)return n(`unsupported_condition`,t);v++,(typeof a[o]!=`string`||!i.has(a[o]))&&n(`unknown_alias`,t),a.hysteresis!=null&&!(g(a.hysteresis)&&a.hysteresis>=0)&&n(`bad_hysteresis`,t),(o===`above`||o===`below`||o===`equals`)&&!g(a.value)&&n(`bad_value`,t),(o===`outside`||o===`inside`)&&!(g(a.lo)&&g(a.hi)&&a.lo<a.hi)&&n(`bad_band`,t),o===`noData`&&!_(a.forS)&&n(`bad_duration`,`${t}.forS`)};return y(r.when,`when`,0),v>16&&n(`too_many_leaves`,`when`),t}function y(e){let t=e.when;for(;t&&typeof t==`object`;)if(`all`in t)t=t.all[0];else if(`any`in t)t=t.any[0];else if(`not`in t)return null;else break;return t?`above`in t||`below`in t?Number.isFinite(t.value)?t.value:null:`outside`in t&&Number.isFinite(t.hi)?t.hi:null:null}export{i,v as n,f as r,y as t};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AnnotationTool, type ChartAnnotation } from '../lib/annotations';
|
|
2
|
+
import type { ChartMark } from '../engine/alerts';
|
|
2
3
|
import { type PlotFrame, type PlotFrameStore } from '../lib/render/plotFrame';
|
|
3
4
|
interface Props {
|
|
4
5
|
annotations: ChartAnnotation[];
|
|
@@ -8,13 +9,17 @@ interface Props {
|
|
|
8
9
|
/** Escape pressed with a tool set. */
|
|
9
10
|
onToolEnd: () => void;
|
|
10
11
|
onSelect: (id: string | null) => void;
|
|
12
|
+
/** Alert marks (read-only, under the annotations); only drawn on a chart with an X frame. */
|
|
13
|
+
marks?: ChartMark[];
|
|
14
|
+
/** A press on a mark's top strip: its rule id. */
|
|
15
|
+
onMarkSelect?: (ruleId: string) => void;
|
|
11
16
|
}
|
|
12
17
|
type Pt = [number, number];
|
|
13
18
|
/** Elements outside the chart marked with this attribute keep the selection when pressed (a host's style controls). */
|
|
14
19
|
export declare const ANNOTATION_CONTROLS_ATTR = "data-kymo-annotation-controls";
|
|
15
20
|
/** Pixel positions of an annotation's points (null: pinned to X on a chart without an X frame now). */
|
|
16
21
|
export declare function annotationPx(a: ChartAnnotation, W: number, H: number, frame: PlotFrame | null): Pt[] | null;
|
|
17
|
-
export declare const AnnotationLayer: import("react").MemoExoticComponent<({ annotations, tool, plotFrame, onChange, onToolEnd, onSelect }: Props) => import("react/jsx-runtime").JSX.Element>;
|
|
22
|
+
export declare const AnnotationLayer: import("react").MemoExoticComponent<({ annotations, tool, plotFrame, onChange, onToolEnd, onSelect, marks: alertList, onMarkSelect }: Props) => import("react/jsx-runtime").JSX.Element>;
|
|
18
23
|
/** A smooth path through the stroke's points (quadratic curves through the midpoints). */
|
|
19
24
|
export declare function brushPath(px: Pt[]): string;
|
|
20
25
|
/** Head of an arrow ending at (x1, y1): its outline and where the shaft should stop. */
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type CameraPanelSettings } from '../lib/panelChartSettings';
|
|
2
|
+
import type { LiveConnection } from '../lib/live/types';
|
|
3
|
+
import { type VideoSource } from '../lib/live/videoTrack';
|
|
4
|
+
import type { LoadedDataset } from '../types/loadedDataset';
|
|
5
|
+
/** The worksheet's shared chart cursor (engine.view.cursorX), in X data units. */
|
|
6
|
+
export interface CameraCursor {
|
|
7
|
+
get(): number | null;
|
|
8
|
+
subscribe(fn: (x: number | null) => void): () => void;
|
|
9
|
+
}
|
|
10
|
+
/** A cloud recording the worksheet shows: its selected recording dataset's wall clock (importer meta) and X shift. */
|
|
11
|
+
export interface CameraRecording {
|
|
12
|
+
recordingId: string;
|
|
13
|
+
/** Unix seconds at X = 0 (info.meta.wallOrigin); null when the recording doesn't say. */
|
|
14
|
+
wallOrigin: number | null;
|
|
15
|
+
wallQuality: 'exact' | 'approx' | null;
|
|
16
|
+
/** The dataset's X offset (engine.datasets offset), subtracted from the cursor. */
|
|
17
|
+
xOffset: number;
|
|
18
|
+
}
|
|
19
|
+
/** The selected datasets that are cloud recordings, in selection order. */
|
|
20
|
+
export declare function cameraRecordings(datasets: readonly LoadedDataset[], offsets: Readonly<Record<string, number>>): CameraRecording[];
|
|
21
|
+
export interface CameraPanelProps {
|
|
22
|
+
settings: CameraPanelSettings;
|
|
23
|
+
/** Every live connection (cameras and data streams). */
|
|
24
|
+
live: LiveConnection[];
|
|
25
|
+
/** Plotted ids: the first selected data stream is the default sync stream. */
|
|
26
|
+
selection: string[];
|
|
27
|
+
/** The worksheet's X range; null = following live. */
|
|
28
|
+
viewXRange: [number, number] | null;
|
|
29
|
+
/** The worksheet's X axis is stream time (only then can a cursor map to a frame). */
|
|
30
|
+
xIsTime: boolean;
|
|
31
|
+
/** The worksheet's X key: a recording maps its 'Time' (or an exact ElapsedTime) to wall time. */
|
|
32
|
+
xKey?: string;
|
|
33
|
+
/** Every video source (engine.video.sources()): recorded cameras play from here. */
|
|
34
|
+
video?: VideoSource[];
|
|
35
|
+
/** The recordings the worksheet shows: a camera recorded with one of them plays recorded instead of live. */
|
|
36
|
+
recordings?: CameraRecording[];
|
|
37
|
+
cursor?: CameraCursor;
|
|
38
|
+
onContextMenu?: (e: {
|
|
39
|
+
clientX: number;
|
|
40
|
+
clientY: number;
|
|
41
|
+
}) => void;
|
|
42
|
+
}
|
|
43
|
+
export declare function CameraPanel(p: CameraPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,6 +7,8 @@ import type { ReactNode, Ref } from 'react';
|
|
|
7
7
|
import type { DashboardPanel } from '../components/dashboardGridLayout';
|
|
8
8
|
import { type PanelChartSettings } from '../lib/panelChartSettings';
|
|
9
9
|
import { type XYZMeshChartHandle } from '../components/XYZMeshChart';
|
|
10
|
+
import { type CameraCursor, type CameraRecording } from './CameraPanel';
|
|
11
|
+
import type { VideoSource } from '../lib/live/videoTrack';
|
|
10
12
|
import type { LiveZoomBus } from '../lib/render/liveZoomBus';
|
|
11
13
|
import { type WorksheetData } from './useWorksheetData';
|
|
12
14
|
import type { LiveConnection } from '../lib/live/types';
|
|
@@ -29,6 +31,8 @@ export interface ChartBodyProps {
|
|
|
29
31
|
height: number;
|
|
30
32
|
viewXRange: [number, number] | null;
|
|
31
33
|
onViewXRangeChange: (r: [number, number] | null) => void;
|
|
34
|
+
/** Multiband hover: the shared cursor X of the worksheet (engine.view.setCursorX). */
|
|
35
|
+
onCursorX?: (x: number | null) => void;
|
|
32
36
|
liveZoom: LiveZoomBus;
|
|
33
37
|
liveWindowSeconds: number | null;
|
|
34
38
|
onLiveWindowChange: (seconds: number | null) => void;
|
|
@@ -36,6 +40,14 @@ export interface ChartBodyProps {
|
|
|
36
40
|
beaconLive: LiveConnection | null;
|
|
37
41
|
/** A loaded dataset or lap by id (beacon reference data). */
|
|
38
42
|
findDataset: (id: string) => LoadedDataset | undefined;
|
|
43
|
+
/** Every live connection and the plotted ids (camera panels). */
|
|
44
|
+
live?: LiveConnection[];
|
|
45
|
+
selection?: string[];
|
|
46
|
+
/** The worksheet's shared cursor (camera panels follow it). */
|
|
47
|
+
cursor?: CameraCursor;
|
|
48
|
+
/** engine.video.sources() and the recordings the worksheet shows (camera panels play recorded cameras). */
|
|
49
|
+
video?: VideoSource[];
|
|
50
|
+
recordings?: CameraRecording[];
|
|
39
51
|
shiftMode?: {
|
|
40
52
|
label: string;
|
|
41
53
|
onDragStart: () => void;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* over — a lap, a battery cycle…) is drawn, with reference data behind it — a
|
|
4
4
|
* loaded dataset, the previous cycle, or nothing (the chart restarts every cycle).
|
|
5
5
|
*/
|
|
6
|
-
import type { LiveChartSource } from '../components/MultiBandCanvasChart';
|
|
6
|
+
import type { LiveChartSource, MultibandEnvelope } from '../components/MultiBandCanvasChart';
|
|
7
7
|
import { type PointColumns } from '../lib/data/pointColumns';
|
|
8
8
|
import type { LiveConnection } from '../lib/live/types';
|
|
9
9
|
import type { LoadedDataset } from '../types/loadedDataset';
|
|
@@ -25,3 +25,6 @@ export declare function datasetReference(ds: LoadedDataset, xKey: string, cols:
|
|
|
25
25
|
export declare function beaconLiveSource(conn: LiveConnection, xKey: string, cols: string[], beacon: BeaconSettings, refDataset: LoadedDataset | undefined): LiveChartSource;
|
|
26
26
|
/** Colour of the active reference, or null when nothing is drawn behind. */
|
|
27
27
|
export declare function referenceColor(beacon: BeaconSettings, hasDataset: boolean): string | null;
|
|
28
|
+
/** A dataset's bands (a reference's envelope) as chart envelopes: for each plotted series whose column has one, shaded
|
|
29
|
+
* in `color` (else the series' own) on the dataset's X column, moved by its offset. */
|
|
30
|
+
export declare function referenceBands(ds: Pick<LoadedDataset, 'data' | 'bands'>, xKey: string, series: PlotSeries[], color?: string, offset?: number): MultibandEnvelope[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Camera panel geometry: when a panel is small enough for a camera's low layer. */
|
|
2
|
+
/** Narrower than this (CSS px) a panel plays a camera's low layer; it goes back to full quality at UP_PX (no flapping). */
|
|
3
|
+
export declare const LOW_BELOW_PX = 640;
|
|
4
|
+
/** Whether a panel `width` px wide is small, given whether it was (hysteresis between LOW_BELOW_PX and UP_PX). */
|
|
5
|
+
export declare function smallPanel(width: number, wasSmall: boolean): boolean;
|
|
6
|
+
/** A wall's grid for `n` cameras: 1, 2 side by side, 2x2 up to 4, 3x3 up to 9. */
|
|
7
|
+
export declare function wallGrid(n: number): {
|
|
8
|
+
cols: number;
|
|
9
|
+
rows: number;
|
|
10
|
+
};
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
import type { LiveChartSource } from '../components/MultiBandCanvasChart';
|
|
8
8
|
import { type PointColumns } from '../lib/data/pointColumns';
|
|
9
9
|
import type { LiveConnection } from '../lib/live/types';
|
|
10
|
+
/** A live stream a multiband chart pulls itself: its series key suffix (`__N`, its overlay index) and X offset. */
|
|
11
|
+
export interface LiveOverlay {
|
|
12
|
+
conn: LiveConnection;
|
|
13
|
+
keySuffix: string;
|
|
14
|
+
offset: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The selected live streams a multiband chart pulls every frame as overlays, or null (a single stream alone has its
|
|
18
|
+
* own path; the rest draw the streams' row snapshots): one stream over in-memory datasets, after them; or several
|
|
19
|
+
* streams alone on time, each on its own clock (X is each one's own time).
|
|
20
|
+
*/
|
|
21
|
+
export declare function liveOverlayStreams(live: LiveConnection[], datasets: number, anyDuckDb: boolean, xKey: string, offsets: Record<string, number>): LiveOverlay[] | null;
|
|
22
|
+
/** {@link liveOverlaySource} for several streams: every stream's layers after the datasets', X over all of them. */
|
|
23
|
+
export declare function liveOverlaysSource(streams: LiveOverlay[], xKey: string, cols: string[], staticLayers: PointColumns[], staticRange: [number, number] | null): LiveChartSource;
|
|
10
24
|
export declare function liveOverlaySource(conn: LiveConnection, xKey: string, cols: string[],
|
|
11
25
|
/** Series key suffix of the stream (`__N`, its overlay index). */
|
|
12
26
|
keySuffix: string,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type KymoscopeEngine } from '../engine/createEngine';
|
|
2
2
|
import { type ChartStatus, type ContextMenuEvent } from './ChartBody';
|
|
3
|
-
import type
|
|
3
|
+
import { type AnnotationTool, type ChartAnnotation } from '../lib/annotations';
|
|
4
|
+
import type { ChartMark } from '../engine/alerts';
|
|
5
|
+
import { type PlotFrame } from '../lib/render/plotFrame';
|
|
4
6
|
export interface ChartStyle {
|
|
5
7
|
/** Plot background (overrides the chart's own background setting). */
|
|
6
8
|
background?: string;
|
|
@@ -26,6 +28,8 @@ export interface ChartViewEvents extends Record<string, unknown> {
|
|
|
26
28
|
annotationtool: AnnotationTool | null;
|
|
27
29
|
/** An annotation was selected in the chart (its id), or the selection ended (null). */
|
|
28
30
|
annotationselect: string | null;
|
|
31
|
+
/** An alert mark's top strip was pressed: the rule's id. */
|
|
32
|
+
markselect: string;
|
|
29
33
|
}
|
|
30
34
|
export interface ChartView {
|
|
31
35
|
readonly chartId: string;
|
|
@@ -61,3 +65,10 @@ export declare function mountChart(engine: KymoscopeEngine, chartId: string, ele
|
|
|
61
65
|
export declare function refreshCharts(engine: KymoscopeEngine): void;
|
|
62
66
|
/** Tear down every chart of an engine (called by engine.dispose()). */
|
|
63
67
|
export declare function disposeCharts(engine: KymoscopeEngine): void;
|
|
68
|
+
/**
|
|
69
|
+
* The alert marks a chart shows: those of a plotted dataset or stream whose rule ran on the chart's X key (D6: a chart
|
|
70
|
+
* on another axis has no time relation). Memoised on its inputs so charts don't redraw for nothing.
|
|
71
|
+
*/
|
|
72
|
+
export declare const chartMarks: (a: ChartMark[], b: string[], c: string) => ChartMark[];
|
|
73
|
+
/** What PNG export paints: the alert marks (under) and the chart's annotations; open marks end at the plot's edge. */
|
|
74
|
+
export declare function exportNotes(notes: ChartAnnotation[], marks: ChartMark[], frame: PlotFrame | null): ChartAnnotation[];
|
|
@@ -51,6 +51,8 @@ export declare function staticXExtent(parts: {
|
|
|
51
51
|
rows: Record<string, unknown>[];
|
|
52
52
|
offset: number;
|
|
53
53
|
}[], xKey: string): [number, number] | null;
|
|
54
|
+
/** Unit per column, plus each column's suffixed key (e.g. "FZ__0") per overlaid source. */
|
|
55
|
+
export declare function overlayColumnUnits(data: Pick<Dataset, 'columnNames' | 'units'>, sources: number): Record<string, string>;
|
|
54
56
|
/**
|
|
55
57
|
* Rows of the current lap on a wrapping X: from the last sample where X drops by
|
|
56
58
|
* more than half its range (lap distance at the finish line). A channel that
|
|
@@ -65,11 +67,7 @@ export declare function useWorksheetData(input: WorksheetDataInput): {
|
|
|
65
67
|
anyDuckDb: boolean;
|
|
66
68
|
duckDbLayers: DuckDbLayerSpec[];
|
|
67
69
|
liveConn: LiveConnection | null;
|
|
68
|
-
|
|
69
|
-
conn: LiveConnection;
|
|
70
|
-
keySuffix: string;
|
|
71
|
-
offset: number;
|
|
72
|
-
} | null;
|
|
70
|
+
liveOverlays: import("./liveOverlay").LiveOverlay[] | null;
|
|
73
71
|
hasLiveSelected: boolean;
|
|
74
72
|
staticXRange: [number, number] | null;
|
|
75
73
|
xUnit: string;
|
|
@@ -35,3 +35,5 @@ export interface GaugeChartProps {
|
|
|
35
35
|
}) => void;
|
|
36
36
|
}
|
|
37
37
|
export declare function GaugeChart({ gauges, live, backgroundToken, onPlotContextMenu }: GaugeChartProps): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
/** Past the alarm: for a line linked to an alert rule, while the rule is active (spec §6.1); else value >= line. */
|
|
39
|
+
export declare const alarmed: (g: Pick<GaugeReading, "value">, al: AlarmLine | null) => boolean;
|
|
@@ -46,7 +46,7 @@ export interface LiveChartSource {
|
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* Per-series X gap threshold: consecutive valid points farther apart than this
|
|
49
|
-
* are not joined by a line. Derived from the series' OWN
|
|
49
|
+
* are not joined by a line. Derived from the series' OWN mean spacing so a
|
|
50
50
|
* co-plotted source at a different sample rate can't shrink it — a global
|
|
51
51
|
* threshold lets a dense live feed collapse the bound below an imported
|
|
52
52
|
* dataset's spacing and shatter that line into disconnected dots.
|
|
@@ -152,5 +152,7 @@ export interface MultiBandCanvasChartProps {
|
|
|
152
152
|
* whenever it changes. Lets an out-of-core parent size its decimation budget
|
|
153
153
|
* to the actual on-screen resolution instead of a fixed overdrawn count. */
|
|
154
154
|
onPlotWidthCss?: (cssWidth: number) => void;
|
|
155
|
+
/** The hovered X in data units (null when the cursor leaves the plot), at most once per frame. */
|
|
156
|
+
onCursorX?: (x: number | null) => void;
|
|
155
157
|
}
|
|
156
|
-
export declare function MultiBandCanvasChart({ xLabel, series, points, columnUnits, height, onPlotContextMenu, backgroundToken, viewXRange, onViewXRangeChange, liveZoom, liveZoomId, fullXRange, shiftMode, xAxisLine, yAxisLine, gridLine, lineWidth, envelopes, showLatestValues, liveSource, liveWindowSeconds, onLiveWindowChange, liveZoomMode, onPlotWidthCss, }: MultiBandCanvasChartProps): import("react/jsx-runtime").JSX.Element;
|
|
158
|
+
export declare function MultiBandCanvasChart({ xLabel, series, points, columnUnits, height, onPlotContextMenu, backgroundToken, viewXRange, onViewXRangeChange, liveZoom, liveZoomId, fullXRange, shiftMode, xAxisLine, yAxisLine, gridLine, lineWidth, envelopes, showLatestValues, liveSource, liveWindowSeconds, onLiveWindowChange, liveZoomMode, onPlotWidthCss, onCursorX, }: MultiBandCanvasChartProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,7 +7,7 @@ export declare const DASHBOARD_ROW_HEIGHT_PX = 4;
|
|
|
7
7
|
export declare const DASHBOARD_GRID_GAP_PX = 2;
|
|
8
8
|
/** Hard cap on the number of charts we auto-arrange. */
|
|
9
9
|
export declare const MAX_DASHBOARD_PANELS = 16;
|
|
10
|
-
export type DashboardPanelKind = 'multiband' | 'xyscatter' | 'xyzmesh' | 'histogram' | 'polar' | 'spectrum' | 'boxplot' | 'spectrogram' | 'heatmap' | 'violin' | 'confusion' | 'roc' | 'losscurve' | 'controlchart' | 'parallelcoords' | 'embedding' | 'flamegraph' | 'barchart' | 'radar' | 'areachart' | 'cdfchart' | 'bubblechart' | 'treemap' | 'sankey' | 'calibration' | 'qqplot' | 'gantt' | 'gauge';
|
|
10
|
+
export type DashboardPanelKind = 'multiband' | 'xyscatter' | 'xyzmesh' | 'histogram' | 'polar' | 'spectrum' | 'boxplot' | 'spectrogram' | 'heatmap' | 'violin' | 'confusion' | 'roc' | 'losscurve' | 'controlchart' | 'parallelcoords' | 'embedding' | 'flamegraph' | 'barchart' | 'radar' | 'areachart' | 'cdfchart' | 'bubblechart' | 'treemap' | 'sankey' | 'calibration' | 'qqplot' | 'gantt' | 'gauge' | 'camera';
|
|
11
11
|
export interface DashboardGridRect {
|
|
12
12
|
x: number;
|
|
13
13
|
y: number;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { PointColumns } from '../lib/data/pointColumns';
|
|
2
|
+
/**
|
|
3
|
+
* Sends the hovered X (or null) at most once per animation frame, the newest
|
|
4
|
+
* value winning. With no frame scheduler (no window: tests, node) it sends at once.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCursorEmitter(send: (x: number | null) => void, schedule?: ((cb: () => void) => void) | null): {
|
|
7
|
+
set(x: number | null): void;
|
|
8
|
+
};
|
|
2
9
|
/** X in data units under a cursor pixel, for the current view. */
|
|
3
10
|
export declare function dataXAtPx(px: number, left: number, plotW: number, xmin: number, xmax: number): number;
|
|
4
11
|
/**
|