mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
slopfixing / issue 002
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
# FIXED!
|
||||||
# Issue 001: Global Input Listener Churn
|
# Issue 001: Global Input Listener Churn
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|||||||
@@ -0,0 +1,849 @@
|
|||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/",
|
||||||
|
"runDir": "perf/results/2026-06-12T06-07-57-124Z-root-runtime",
|
||||||
|
"environment": {
|
||||||
|
"cpuThrottle": 6,
|
||||||
|
"viewport": {
|
||||||
|
"width": 390,
|
||||||
|
"height": 844
|
||||||
|
},
|
||||||
|
"isMobile": true,
|
||||||
|
"sampleMs": 10000
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"Timestamp": 23292.158609,
|
||||||
|
"AudioHandlers": 0,
|
||||||
|
"AudioWorkletProcessors": 0,
|
||||||
|
"Documents": 3,
|
||||||
|
"Frames": 1,
|
||||||
|
"JSEventListeners": 398,
|
||||||
|
"LayoutObjects": 1945,
|
||||||
|
"MediaKeySessions": 0,
|
||||||
|
"MediaKeys": 0,
|
||||||
|
"Nodes": 2266,
|
||||||
|
"Resources": 18,
|
||||||
|
"ContextLifecycleStateObservers": 13,
|
||||||
|
"V8PerContextDatas": 4,
|
||||||
|
"WorkerGlobalScopes": 0,
|
||||||
|
"UACSSResources": 0,
|
||||||
|
"RTCPeerConnections": 0,
|
||||||
|
"ResourceFetchers": 3,
|
||||||
|
"AdSubframes": 0,
|
||||||
|
"DetachedScriptStates": 2,
|
||||||
|
"ArrayBufferContents": 3,
|
||||||
|
"LayoutCount": 12,
|
||||||
|
"RecalcStyleCount": 30,
|
||||||
|
"LayoutDuration": 0.928481,
|
||||||
|
"RecalcStyleDuration": 0.991796,
|
||||||
|
"DevToolsCommandDuration": 0.000323,
|
||||||
|
"ScriptDuration": 6.326745,
|
||||||
|
"V8CompileDuration": 0,
|
||||||
|
"TaskDuration": 11.079249,
|
||||||
|
"TaskOtherDuration": 2.831904,
|
||||||
|
"ThreadTime": 11.911973,
|
||||||
|
"ProcessTime": 14.42,
|
||||||
|
"JSHeapUsedSize": 8961292,
|
||||||
|
"JSHeapTotalSize": 12058624,
|
||||||
|
"FirstMeaningfulPaint": 0,
|
||||||
|
"DomContentLoaded": 23282.130574,
|
||||||
|
"NavigationStart": 23279.964199
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"elapsedMs": 20101.5,
|
||||||
|
"listeners": [
|
||||||
|
{
|
||||||
|
"key": "add:input:invalid",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:window:online",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:document:visibilitychange",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:window:online",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:document:visibilitychange",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:canplay",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:ended",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:error",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:pause",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:play",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:stalled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:waiting",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:auxclick",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:click",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:contextmenu",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:copy",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:cut",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:drag",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragend",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragenter",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragexit",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragleave",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragover",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragstart",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:drop",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:gotpointercapture",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:input",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keydown",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keypress",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keyup",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:lostpointercapture",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mousedown",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mousemove",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mouseout",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mouseover",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mouseup",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:paste",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:pointercancel",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:pointerdown",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:pointermove",
|
||||||
|
"count": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timers": [
|
||||||
|
{
|
||||||
|
"key": "timeout:0",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:250",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:60000",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:12000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:200",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:15000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:1000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:2000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:4000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:10000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:3000",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeouts": 10,
|
||||||
|
"intervals": 12,
|
||||||
|
"timeoutFires": 4,
|
||||||
|
"intervalFires": 45,
|
||||||
|
"webSocketMessageCount": 285,
|
||||||
|
"webSocketBytes": 5610397,
|
||||||
|
"webSocketEvents": [
|
||||||
|
{
|
||||||
|
"event": "log:entry",
|
||||||
|
"count": 150,
|
||||||
|
"bytes": 35714
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "sensorFrame",
|
||||||
|
"count": 76,
|
||||||
|
"bytes": 142196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "session:sync",
|
||||||
|
"count": 18,
|
||||||
|
"bytes": 306294
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "commandAck",
|
||||||
|
"count": 12,
|
||||||
|
"bytes": 1128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "",
|
||||||
|
"count": 4,
|
||||||
|
"bytes": 4992972
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverSnapshot:frame",
|
||||||
|
"count": 3,
|
||||||
|
"bytes": 291
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "0{\"sid\":\"CQG",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "40{\"sid\":\"-s",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "mode",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 27
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "controlGranted",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "alert",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "alert:new",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 165
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "auth:role",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "rovers",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 5453
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "chat:init",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 77613
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "overseer:memory",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 520
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "log:init",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 44230
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "barcode:state",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 1627
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "kinect:pointCloudFrame",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 262
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "kinect:colorFrame",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 169
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "430[{\"succes",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "431[{\"succes",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 194
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "432[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 133
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverHostStats",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 766
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "2",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "433[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverSnapshot:status",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 79
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "WALL-E",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 40
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mutationsTotal": {
|
||||||
|
"records": 4729,
|
||||||
|
"added": 175,
|
||||||
|
"removed": 11,
|
||||||
|
"attrs": 4524,
|
||||||
|
"text": 19
|
||||||
|
},
|
||||||
|
"mutationTargets": [
|
||||||
|
{
|
||||||
|
"key": "input.accent-emerald-400:attributes:name",
|
||||||
|
"count": 1866
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-emerald-400:attributes:type",
|
||||||
|
"count": 933
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.h-full:attributes:style",
|
||||||
|
"count": 580
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:d",
|
||||||
|
"count": 292
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:fill",
|
||||||
|
"count": 290
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:name",
|
||||||
|
"count": 242
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:type",
|
||||||
|
"count": 121
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.flex-1:attributes:name",
|
||||||
|
"count": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:name",
|
||||||
|
"count": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Text:characterData:",
|
||||||
|
"count": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.chat-composer-input:attributes:name",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-cyan-500:attributes:name",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input:attributes:name",
|
||||||
|
"count": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:type",
|
||||||
|
"count": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "section.panel-section.border:attributes:style",
|
||||||
|
"count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.min-w-0.flex-1:attributes:name",
|
||||||
|
"count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-cyan-500:attributes:type",
|
||||||
|
"count": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "header.flex.items-center:attributes:style",
|
||||||
|
"count": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input:attributes:type",
|
||||||
|
"count": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:stroke",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.flex:attributes:disabled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.min-h-9:attributes:disabled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:value",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "span.inline-flex.items-center:attributes:class",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.flex-1:attributes:value",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:playsinline",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:autoplay",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:disableremoteplayback",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.rounded-sm.px-2:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.flex.flex-col:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:placeholder",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.button-dark.min-w-0:attributes:disabled",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.fixed.bottom-2:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.flex:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.mobile-touch-control.flex:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:value",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.button-dark.text-sm:attributes:disabled",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "audio:attributes:autoplay",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "audio:attributes:disableremoteplayback",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mutationBursts": [
|
||||||
|
{
|
||||||
|
"at": 7974,
|
||||||
|
"records": 241,
|
||||||
|
"added": 123,
|
||||||
|
"removed": 6,
|
||||||
|
"attrs": 110,
|
||||||
|
"text": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 11575,
|
||||||
|
"records": 147,
|
||||||
|
"added": 11,
|
||||||
|
"removed": 2,
|
||||||
|
"attrs": 132,
|
||||||
|
"text": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 12385,
|
||||||
|
"records": 131,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 127,
|
||||||
|
"text": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 12981,
|
||||||
|
"records": 119,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 119,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 18332,
|
||||||
|
"records": 117,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 116,
|
||||||
|
"text": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 17140,
|
||||||
|
"records": 111,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 111,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 13371,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 13636,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 13804,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 14271,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15643,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15909,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 18503,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15238,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15283,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15415,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15495,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 15947,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 16169,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 16317,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frames": {
|
||||||
|
"count": 86,
|
||||||
|
"average": 206.1918604651163,
|
||||||
|
"p95": 566.5999999999995,
|
||||||
|
"p99": 3399.8999999999996
|
||||||
|
},
|
||||||
|
"longTasks": {
|
||||||
|
"count": 61,
|
||||||
|
"top": [
|
||||||
|
{
|
||||||
|
"startTime": 5290.5,
|
||||||
|
"duration": 2689,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 365.4000000022352,
|
||||||
|
"duration": 1807,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 2182.900000002235,
|
||||||
|
"duration": 1038,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 10371.10000000149,
|
||||||
|
"duration": 709,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 9852,
|
||||||
|
"duration": 519,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 8202.60000000149,
|
||||||
|
"duration": 394,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 3221.699999999255,
|
||||||
|
"duration": 385,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 3882.5,
|
||||||
|
"duration": 356,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 11221.5,
|
||||||
|
"duration": 353,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 18042.900000002235,
|
||||||
|
"duration": 292,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 12110.900000002235,
|
||||||
|
"duration": 276,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 12738.800000000745,
|
||||||
|
"duration": 243,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 16946.5,
|
||||||
|
"duration": 195,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 11575.5,
|
||||||
|
"duration": 186,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 3615.60000000149,
|
||||||
|
"duration": 185,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 14671.699999999255,
|
||||||
|
"duration": 160,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 4239.199999999255,
|
||||||
|
"duration": 130,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 14837.5,
|
||||||
|
"duration": 110,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 15717.60000000149,
|
||||||
|
"duration": 108,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 17352.400000002235,
|
||||||
|
"duration": 107,
|
||||||
|
"name": "self"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"consoleErrors": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 680 KiB |
@@ -0,0 +1,900 @@
|
|||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/",
|
||||||
|
"runDir": "perf/results/2026-06-12T06-09-03-973Z-root-runtime",
|
||||||
|
"environment": {
|
||||||
|
"cpuThrottle": 8,
|
||||||
|
"viewport": {
|
||||||
|
"width": 390,
|
||||||
|
"height": 844
|
||||||
|
},
|
||||||
|
"isMobile": true,
|
||||||
|
"sampleMs": 30000
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"Timestamp": 23379.874681,
|
||||||
|
"AudioHandlers": 0,
|
||||||
|
"AudioWorkletProcessors": 0,
|
||||||
|
"Documents": 3,
|
||||||
|
"Frames": 1,
|
||||||
|
"JSEventListeners": 683,
|
||||||
|
"LayoutObjects": 1975,
|
||||||
|
"MediaKeySessions": 0,
|
||||||
|
"MediaKeys": 0,
|
||||||
|
"Nodes": 2710,
|
||||||
|
"Resources": 20,
|
||||||
|
"ContextLifecycleStateObservers": 20,
|
||||||
|
"V8PerContextDatas": 4,
|
||||||
|
"WorkerGlobalScopes": 0,
|
||||||
|
"UACSSResources": 0,
|
||||||
|
"RTCPeerConnections": 4,
|
||||||
|
"ResourceFetchers": 3,
|
||||||
|
"AdSubframes": 0,
|
||||||
|
"DetachedScriptStates": 2,
|
||||||
|
"ArrayBufferContents": 12,
|
||||||
|
"LayoutCount": 49,
|
||||||
|
"RecalcStyleCount": 77,
|
||||||
|
"LayoutDuration": 1.49198,
|
||||||
|
"RecalcStyleDuration": 1.886372,
|
||||||
|
"DevToolsCommandDuration": 0.000349,
|
||||||
|
"ScriptDuration": 14.419283,
|
||||||
|
"V8CompileDuration": 0,
|
||||||
|
"TaskDuration": 29.584723,
|
||||||
|
"TaskOtherDuration": 11.786739,
|
||||||
|
"ThreadTime": 32.430498,
|
||||||
|
"ProcessTime": 38.63,
|
||||||
|
"JSHeapUsedSize": 7496328,
|
||||||
|
"JSHeapTotalSize": 13107200,
|
||||||
|
"FirstMeaningfulPaint": 0,
|
||||||
|
"DomContentLoaded": 23349.78059,
|
||||||
|
"NavigationStart": 23346.788167
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"elapsedMs": 38566.699999999255,
|
||||||
|
"listeners": [
|
||||||
|
{
|
||||||
|
"key": "add:input:invalid",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:window:online",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:document:visibilitychange",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:canplay",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:ended",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:error",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:pause",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:play",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:stalled",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:waiting",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:window:online",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:document:visibilitychange",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:play",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:pause",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:ended",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:error",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:waiting",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:canplay",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:stalled",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:WebSocket:message",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:auxclick",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:click",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:contextmenu",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:copy",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:cut",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:drag",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragend",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragenter",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragexit",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragleave",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragover",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragstart",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:drop",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:gotpointercapture",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:input",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keydown",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keypress",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keyup",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:lostpointercapture",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mousedown",
|
||||||
|
"count": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timers": [
|
||||||
|
{
|
||||||
|
"key": "timeout:0",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:250",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:200",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:2000",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:12000",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:60000",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:15000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:4000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:3000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:1000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:2000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:10000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:1282",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:1257",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeouts": 19,
|
||||||
|
"intervals": 15,
|
||||||
|
"timeoutFires": 10,
|
||||||
|
"intervalFires": 121,
|
||||||
|
"webSocketMessageCount": 533,
|
||||||
|
"webSocketBytes": 11199188,
|
||||||
|
"webSocketEvents": [
|
||||||
|
{
|
||||||
|
"event": "log:entry",
|
||||||
|
"count": 308,
|
||||||
|
"bytes": 71889
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "sensorFrame",
|
||||||
|
"count": 90,
|
||||||
|
"bytes": 168271
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "commandAck",
|
||||||
|
"count": 36,
|
||||||
|
"bytes": 3396
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "session:sync",
|
||||||
|
"count": 33,
|
||||||
|
"bytes": 560916
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "",
|
||||||
|
"count": 14,
|
||||||
|
"bytes": 10130394
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverSnapshot:frame",
|
||||||
|
"count": 10,
|
||||||
|
"bytes": 967
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "mode",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 54
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "controlGranted",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 82
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "alert",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "alert:new",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "auth:role",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "rovers",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 10906
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "chat:init",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 155226
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "overseer:memory",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 1040
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "log:init",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 89130
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "barcode:state",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 3254
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "kinect:pointCloudFrame",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 524
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "kinect:colorFrame",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 338
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverSnapshot:status",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 156
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "0{\"sid\":\"a3i",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "40{\"sid\":\"Bp",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "430[{\"succes",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "431[{\"succes",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 194
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "2",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "432[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 133
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "433[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "WALL-E",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "0{\"sid\":\"3kq",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "40{\"sid\":\"BX",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "436[{\"error\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "437[{\"error\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "438[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 133
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "439[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "Freaky",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverHostStats",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 767
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mutationsTotal": {
|
||||||
|
"records": 6037,
|
||||||
|
"added": 199,
|
||||||
|
"removed": 35,
|
||||||
|
"attrs": 5767,
|
||||||
|
"text": 36
|
||||||
|
},
|
||||||
|
"mutationTargets": [
|
||||||
|
{
|
||||||
|
"key": "input.accent-emerald-400:attributes:name",
|
||||||
|
"count": 2648
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-emerald-400:attributes:type",
|
||||||
|
"count": 1324
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.h-full:attributes:style",
|
||||||
|
"count": 512
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:name",
|
||||||
|
"count": 338
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:d",
|
||||||
|
"count": 262
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:fill",
|
||||||
|
"count": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:type",
|
||||||
|
"count": 169
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Text:characterData:",
|
||||||
|
"count": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.flex-1:attributes:name",
|
||||||
|
"count": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:name",
|
||||||
|
"count": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-cyan-500:attributes:name",
|
||||||
|
"count": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "section.panel-section.border:attributes:style",
|
||||||
|
"count": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.chat-composer-input:attributes:name",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "header.flex.items-center:attributes:style",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:stroke",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input:attributes:name",
|
||||||
|
"count": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:type",
|
||||||
|
"count": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-cyan-500:attributes:type",
|
||||||
|
"count": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.min-w-0.flex-1:attributes:name",
|
||||||
|
"count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input:attributes:type",
|
||||||
|
"count": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.rounded-sm.px-2:attributes:class",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.fixed.bottom-2:attributes:class",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:value",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "span.inline-flex.items-center:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.flex:attributes:disabled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.min-h-9:attributes:disabled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.flex:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.mobile-touch-control.flex:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:placeholder",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:playsinline",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:autoplay",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:disableremoteplayback",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "audio:attributes:autoplay",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "audio:attributes:disableremoteplayback",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.flex-1:attributes:value",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "span.inline-block.rounded:attributes:style",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.flex.flex-col:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.button-dark.min-w-0:attributes:disabled",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:value",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.button-dark.text-sm:attributes:disabled",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mutationBursts": [
|
||||||
|
{
|
||||||
|
"at": 10936,
|
||||||
|
"records": 241,
|
||||||
|
"added": 123,
|
||||||
|
"removed": 6,
|
||||||
|
"attrs": 110,
|
||||||
|
"text": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 27789,
|
||||||
|
"records": 177,
|
||||||
|
"added": 9,
|
||||||
|
"removed": 17,
|
||||||
|
"attrs": 145,
|
||||||
|
"text": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 17412,
|
||||||
|
"records": 133,
|
||||||
|
"added": 2,
|
||||||
|
"removed": 2,
|
||||||
|
"attrs": 118,
|
||||||
|
"text": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 31614,
|
||||||
|
"records": 111,
|
||||||
|
"added": 2,
|
||||||
|
"removed": 1,
|
||||||
|
"attrs": 104,
|
||||||
|
"text": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 37740,
|
||||||
|
"records": 100,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 1,
|
||||||
|
"attrs": 99,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 38360,
|
||||||
|
"records": 99,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 99,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 17915,
|
||||||
|
"records": 59,
|
||||||
|
"added": 11,
|
||||||
|
"removed": 2,
|
||||||
|
"attrs": 45,
|
||||||
|
"text": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 19317,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 20163,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 20363,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 23753,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 24543,
|
||||||
|
"records": 51,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 51,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 31831,
|
||||||
|
"records": 51,
|
||||||
|
"added": 11,
|
||||||
|
"removed": 2,
|
||||||
|
"attrs": 37,
|
||||||
|
"text": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 18579,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 18763,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 19129,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 19374,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 21234,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 22011,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 22767,
|
||||||
|
"records": 47,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 47,
|
||||||
|
"text": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frames": {
|
||||||
|
"count": 181,
|
||||||
|
"average": 185.53591160220995,
|
||||||
|
"p95": 349.90000000000146,
|
||||||
|
"p99": 1849.9000000000015
|
||||||
|
},
|
||||||
|
"longTasks": {
|
||||||
|
"count": 165,
|
||||||
|
"top": [
|
||||||
|
{
|
||||||
|
"startTime": 7151.60000000149,
|
||||||
|
"duration": 3795,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 459.6000000014901,
|
||||||
|
"duration": 2544,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 3019.199999999255,
|
||||||
|
"duration": 1426,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 29673.900000002235,
|
||||||
|
"duration": 1066,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 14211,
|
||||||
|
"duration": 972,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 28901.800000000745,
|
||||||
|
"duration": 772,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 13490.699999999255,
|
||||||
|
"duration": 720,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 16782.400000002235,
|
||||||
|
"duration": 639,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 11240.199999999255,
|
||||||
|
"duration": 552,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 31092.5,
|
||||||
|
"duration": 524,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 4445.60000000149,
|
||||||
|
"duration": 495,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 5394.800000000745,
|
||||||
|
"duration": 459,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 27348.199999999255,
|
||||||
|
"duration": 445,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 24835.10000000149,
|
||||||
|
"duration": 363,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 4963.800000000745,
|
||||||
|
"duration": 272,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 37513.60000000149,
|
||||||
|
"duration": 228,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 17919.5,
|
||||||
|
"duration": 215,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 27801.60000000149,
|
||||||
|
"duration": 193,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 17425.10000000149,
|
||||||
|
"duration": 179,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 31885.900000002235,
|
||||||
|
"duration": 176,
|
||||||
|
"name": "self"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"consoleErrors": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 703 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
@@ -0,0 +1,529 @@
|
|||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/",
|
||||||
|
"runDir": "perf/results/2026-06-12T06-11-56-994Z",
|
||||||
|
"elapsedMs": 83940,
|
||||||
|
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/148.0.7778.96 Safari/537.36",
|
||||||
|
"title": "Roomba Rover",
|
||||||
|
"finalUrl": "http://127.0.0.1:4173/",
|
||||||
|
"environment": {
|
||||||
|
"cpuThrottle": 8,
|
||||||
|
"viewport": {
|
||||||
|
"width": 390,
|
||||||
|
"height": 844
|
||||||
|
},
|
||||||
|
"isMobile": true
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"afterLoad": {
|
||||||
|
"Timestamp": 23527.948042,
|
||||||
|
"Documents": 3,
|
||||||
|
"Frames": 1,
|
||||||
|
"JSEventListeners": 267,
|
||||||
|
"Nodes": 651,
|
||||||
|
"LayoutCount": 5,
|
||||||
|
"RecalcStyleCount": 7,
|
||||||
|
"LayoutDuration": 0.500736,
|
||||||
|
"RecalcStyleDuration": 0.283234,
|
||||||
|
"ScriptDuration": 5.693685,
|
||||||
|
"TaskDuration": 7.662781,
|
||||||
|
"JSHeapUsedSize": 4764568,
|
||||||
|
"JSHeapTotalSize": 7077888
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"Timestamp": 23593.417486,
|
||||||
|
"Documents": 1,
|
||||||
|
"Frames": 1,
|
||||||
|
"JSEventListeners": 1032,
|
||||||
|
"Nodes": 2696,
|
||||||
|
"LayoutCount": 137,
|
||||||
|
"RecalcStyleCount": 280,
|
||||||
|
"LayoutDuration": 3.566888,
|
||||||
|
"RecalcStyleDuration": 3.703827,
|
||||||
|
"ScriptDuration": 27.965461,
|
||||||
|
"TaskDuration": 67.992237,
|
||||||
|
"JSHeapUsedSize": 12832824,
|
||||||
|
"JSHeapTotalSize": 19759104
|
||||||
|
},
|
||||||
|
"delta": {
|
||||||
|
"Timestamp": 65.46944399999848,
|
||||||
|
"Documents": -2,
|
||||||
|
"Frames": 0,
|
||||||
|
"JSEventListeners": 765,
|
||||||
|
"Nodes": 2045,
|
||||||
|
"LayoutCount": 132,
|
||||||
|
"RecalcStyleCount": 273,
|
||||||
|
"LayoutDuration": 3.066152,
|
||||||
|
"RecalcStyleDuration": 3.420593,
|
||||||
|
"ScriptDuration": 22.271776000000003,
|
||||||
|
"TaskDuration": 60.329456,
|
||||||
|
"JSHeapUsedSize": 8068256,
|
||||||
|
"JSHeapTotalSize": 12681216
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpuSummary": {
|
||||||
|
"totalProfileMs": 55106.451,
|
||||||
|
"sampleCount": 40511,
|
||||||
|
"topSelfTime": [
|
||||||
|
{
|
||||||
|
"url": "",
|
||||||
|
"functionName": "(program)",
|
||||||
|
"lineNumber": -1,
|
||||||
|
"columnNumber": -1,
|
||||||
|
"selfMs": 26825.782999999923,
|
||||||
|
"samples": 18512
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "",
|
||||||
|
"functionName": "(idle)",
|
||||||
|
"lineNumber": -1,
|
||||||
|
"columnNumber": -1,
|
||||||
|
"selfMs": 4815.981000000005,
|
||||||
|
"samples": 3701
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "(anonymous)",
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 40267,
|
||||||
|
"selfMs": 2656.4819999999972,
|
||||||
|
"samples": 2026
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "ad",
|
||||||
|
"lineNumber": 9,
|
||||||
|
"columnNumber": 7898,
|
||||||
|
"selfMs": 1032.9479999999999,
|
||||||
|
"samples": 818
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "Kt",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 48104,
|
||||||
|
"selfMs": 951.7199999999993,
|
||||||
|
"samples": 747
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "Ce",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 94317,
|
||||||
|
"selfMs": 905.2510000000005,
|
||||||
|
"samples": 727
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "tryParse",
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 49539,
|
||||||
|
"selfMs": 592.3200000000003,
|
||||||
|
"samples": 480
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "",
|
||||||
|
"functionName": "(garbage collector)",
|
||||||
|
"lineNumber": -1,
|
||||||
|
"columnNumber": -1,
|
||||||
|
"selfMs": 578.5280000000001,
|
||||||
|
"samples": 430
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "start",
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 1485400,
|
||||||
|
"selfMs": 445.5410000000001,
|
||||||
|
"samples": 322
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "G4",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 28256,
|
||||||
|
"selfMs": 432.7939999999998,
|
||||||
|
"samples": 325
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "lt",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 7348,
|
||||||
|
"selfMs": 360.4799999999999,
|
||||||
|
"samples": 279
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "ga",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 10651,
|
||||||
|
"selfMs": 330.2019999999997,
|
||||||
|
"samples": 260
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "r",
|
||||||
|
"lineNumber": 1,
|
||||||
|
"columnNumber": 967,
|
||||||
|
"selfMs": 323.32300000000004,
|
||||||
|
"samples": 239
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "Rl",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 101212,
|
||||||
|
"selfMs": 308.80500000000006,
|
||||||
|
"samples": 225
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "S1",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 40721,
|
||||||
|
"selfMs": 305.08099999999996,
|
||||||
|
"samples": 243
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "C2",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 49311,
|
||||||
|
"selfMs": 304.76900000000006,
|
||||||
|
"samples": 250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "ws.onmessage",
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 33453,
|
||||||
|
"selfMs": 284.5850000000001,
|
||||||
|
"samples": 204
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "N2.emit",
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 25371,
|
||||||
|
"selfMs": 275.96699999999987,
|
||||||
|
"samples": 219
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "Xl",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 117224,
|
||||||
|
"selfMs": 260.1920000000001,
|
||||||
|
"samples": 208
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "pa",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 10399,
|
||||||
|
"selfMs": 249.32600000000016,
|
||||||
|
"samples": 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "S4",
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 91365,
|
||||||
|
"selfMs": 237.0250000000001,
|
||||||
|
"samples": 179
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "N6",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 34144,
|
||||||
|
"selfMs": 234.33099999999988,
|
||||||
|
"samples": 188
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "bl",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 80838,
|
||||||
|
"selfMs": 228.66600000000017,
|
||||||
|
"samples": 171
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "o2",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 42232,
|
||||||
|
"selfMs": 219.2690000000001,
|
||||||
|
"samples": 182
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "",
|
||||||
|
"functionName": "setAttribute",
|
||||||
|
"lineNumber": -1,
|
||||||
|
"columnNumber": -1,
|
||||||
|
"selfMs": 213.44599999999997,
|
||||||
|
"samples": 178
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "",
|
||||||
|
"functionName": "(anonymous)",
|
||||||
|
"lineNumber": 29,
|
||||||
|
"columnNumber": 16,
|
||||||
|
"selfMs": 194.01300000000003,
|
||||||
|
"samples": 148
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "s2",
|
||||||
|
"lineNumber": 9,
|
||||||
|
"columnNumber": 71,
|
||||||
|
"selfMs": 189.78200000000004,
|
||||||
|
"samples": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "na",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 3730,
|
||||||
|
"selfMs": 184.6409999999999,
|
||||||
|
"samples": 146
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "Eh",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 95737,
|
||||||
|
"selfMs": 179.85799999999998,
|
||||||
|
"samples": 138
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"functionName": "Oh",
|
||||||
|
"lineNumber": 8,
|
||||||
|
"columnNumber": 116259,
|
||||||
|
"selfMs": 179.09700000000007,
|
||||||
|
"samples": 134
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"responsiveness": {
|
||||||
|
"longTaskCount": 333,
|
||||||
|
"topLongTasks": [
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 7254.699999999255,
|
||||||
|
"duration": 3731
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 409.19999999925494,
|
||||||
|
"duration": 2518
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unknown",
|
||||||
|
"startTime": 13437.400000002235,
|
||||||
|
"duration": 1801
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 2960.199999999255,
|
||||||
|
"duration": 1382
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 24715.300000000745,
|
||||||
|
"duration": 1038
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 54470.699999999255,
|
||||||
|
"duration": 1002
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 53685.199999999255,
|
||||||
|
"duration": 785
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 23959.699999999255,
|
||||||
|
"duration": 755
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 50997,
|
||||||
|
"duration": 747
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 4343.10000000149,
|
||||||
|
"duration": 621
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 27087.5,
|
||||||
|
"duration": 597
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 22357.300000000745,
|
||||||
|
"duration": 554
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unknown",
|
||||||
|
"startTime": 15496,
|
||||||
|
"duration": 478
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 5324.5,
|
||||||
|
"duration": 474
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 11302.300000000745,
|
||||||
|
"duration": 448
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 40393.300000000745,
|
||||||
|
"duration": 435
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 55742.5,
|
||||||
|
"duration": 405
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 25906.300000000745,
|
||||||
|
"duration": 323
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 38613.5,
|
||||||
|
"duration": 303
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "self",
|
||||||
|
"startTime": 46738.5,
|
||||||
|
"duration": 286
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameCount": 358,
|
||||||
|
"averageFrameMs": 207.5804469273743,
|
||||||
|
"p95FrameMs": 483.3000000000029,
|
||||||
|
"p99FrameMs": 2016.6,
|
||||||
|
"intervalDriftCount": 43,
|
||||||
|
"averageIntervalDriftMs": 1537.434883720913,
|
||||||
|
"p95IntervalDriftMs": 7938.5,
|
||||||
|
"p99IntervalDriftMs": 8055.300000000745
|
||||||
|
},
|
||||||
|
"consoleMessages": [
|
||||||
|
{
|
||||||
|
"type": "info",
|
||||||
|
"text": "[socket] connecting to https://rover.otter.land",
|
||||||
|
"location": {
|
||||||
|
"url": "http://127.0.0.1:4173/assets/index-BcKZ4JVW.js",
|
||||||
|
"line": 11,
|
||||||
|
"column": 63824,
|
||||||
|
"lineNumber": 11,
|
||||||
|
"columnNumber": 63824
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"text": "Failed to load resource: the server responded with a status of 404 ()",
|
||||||
|
"location": {
|
||||||
|
"url": "https://media.discordapp.net/attachments/1424887548622405672/1512626709692682340/cat2.PNG?ex=6a24c6e6&is=6a237566&hm=baeec4c8e666888d89711065b1dbcb391c3415f9c28d1917bdd286776b60c867&=&format=webp&quality=lossless",
|
||||||
|
"line": 0,
|
||||||
|
"column": 0,
|
||||||
|
"lineNumber": 0,
|
||||||
|
"columnNumber": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"text": "Failed to load resource: the server responded with a status of 400 ()",
|
||||||
|
"location": {
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"line": 0,
|
||||||
|
"column": 0,
|
||||||
|
"lineNumber": 0,
|
||||||
|
"columnNumber": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"text": "Failed to load resource: the server responded with a status of 400 ()",
|
||||||
|
"location": {
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble-audio/whep",
|
||||||
|
"line": 0,
|
||||||
|
"column": 0,
|
||||||
|
"lineNumber": 0,
|
||||||
|
"columnNumber": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"text": "Failed to load resource: the server responded with a status of 400 ()",
|
||||||
|
"location": {
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"line": 0,
|
||||||
|
"column": 0,
|
||||||
|
"lineNumber": 0,
|
||||||
|
"columnNumber": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"text": "Failed to load resource: the server responded with a status of 400 ()",
|
||||||
|
"location": {
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"line": 0,
|
||||||
|
"column": 0,
|
||||||
|
"lineNumber": 0,
|
||||||
|
"columnNumber": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"failedRequests": [
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"method": "POST",
|
||||||
|
"failure": "net::ERR_ABORTED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble-audio/whep",
|
||||||
|
"method": "POST",
|
||||||
|
"failure": "net::ERR_ABORTED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"method": "POST",
|
||||||
|
"failure": "net::ERR_ABORTED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"errorResponses": [
|
||||||
|
{
|
||||||
|
"url": "https://media.discordapp.net/attachments/1424887548622405672/1512626709692682340/cat2.PNG?ex=6a24c6e6&is=6a237566&hm=baeec4c8e666888d89711065b1dbcb391c3415f9c28d1917bdd286776b60c867&=&format=webp&quality=lossless",
|
||||||
|
"status": 404
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"status": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble-audio/whep",
|
||||||
|
"status": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"status": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/video/Bweeble/whep",
|
||||||
|
"status": 400
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 461 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,896 @@
|
|||||||
|
{
|
||||||
|
"url": "https://rover.otter.land/",
|
||||||
|
"runDir": "perf/results/2026-06-12T06-15-19-037Z-root-runtime",
|
||||||
|
"environment": {
|
||||||
|
"cpuThrottle": 8,
|
||||||
|
"viewport": {
|
||||||
|
"width": 390,
|
||||||
|
"height": 844
|
||||||
|
},
|
||||||
|
"isMobile": true,
|
||||||
|
"sampleMs": 30000
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"Timestamp": 23755.664379,
|
||||||
|
"AudioHandlers": 0,
|
||||||
|
"AudioWorkletProcessors": 0,
|
||||||
|
"Documents": 3,
|
||||||
|
"Frames": 1,
|
||||||
|
"JSEventListeners": 1280,
|
||||||
|
"LayoutObjects": 1996,
|
||||||
|
"MediaKeySessions": 0,
|
||||||
|
"MediaKeys": 0,
|
||||||
|
"Nodes": 2628,
|
||||||
|
"Resources": 20,
|
||||||
|
"ContextLifecycleStateObservers": 20,
|
||||||
|
"V8PerContextDatas": 4,
|
||||||
|
"WorkerGlobalScopes": 0,
|
||||||
|
"UACSSResources": 0,
|
||||||
|
"RTCPeerConnections": 4,
|
||||||
|
"ResourceFetchers": 3,
|
||||||
|
"AdSubframes": 0,
|
||||||
|
"DetachedScriptStates": 2,
|
||||||
|
"ArrayBufferContents": 14,
|
||||||
|
"LayoutCount": 43,
|
||||||
|
"RecalcStyleCount": 80,
|
||||||
|
"LayoutDuration": 1.465119,
|
||||||
|
"RecalcStyleDuration": 1.54029,
|
||||||
|
"DevToolsCommandDuration": 0.00038,
|
||||||
|
"ScriptDuration": 15.671196,
|
||||||
|
"V8CompileDuration": 0,
|
||||||
|
"TaskDuration": 30.021546,
|
||||||
|
"TaskOtherDuration": 11.344561,
|
||||||
|
"ThreadTime": 32.951976,
|
||||||
|
"ProcessTime": 39.43,
|
||||||
|
"JSHeapUsedSize": 7054604,
|
||||||
|
"JSHeapTotalSize": 13893632,
|
||||||
|
"FirstMeaningfulPaint": 0,
|
||||||
|
"DomContentLoaded": 23725.629234,
|
||||||
|
"NavigationStart": 23721.84567
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"elapsedMs": 53572.199999999255,
|
||||||
|
"listeners": [
|
||||||
|
{
|
||||||
|
"key": "add:input:invalid",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:window:online",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:document:visibilitychange",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:canplay",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:ended",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:error",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:pause",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:play",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:stalled",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:audio:waiting",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:window:online",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:document:visibilitychange",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:play",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:pause",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:ended",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:error",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:waiting",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:canplay",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "remove:audio:stalled",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:WebSocket:message",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:auxclick",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:click",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:contextmenu",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:copy",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:cut",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:drag",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragend",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragenter",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragexit",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragleave",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragover",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:dragstart",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:drop",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:gotpointercapture",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:input",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keydown",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keypress",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:keyup",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:lostpointercapture",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "add:div#root:mousedown",
|
||||||
|
"count": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timers": [
|
||||||
|
{
|
||||||
|
"key": "timeout:0",
|
||||||
|
"count": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:12000",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:60000",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:250",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:200",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:2000",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:15000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:4000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:3000",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:1000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "interval:2000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:10000",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timeout:1491",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeouts": 19,
|
||||||
|
"intervals": 16,
|
||||||
|
"timeoutFires": 11,
|
||||||
|
"intervalFires": 136,
|
||||||
|
"webSocketMessageCount": 498,
|
||||||
|
"webSocketBytes": 11209457,
|
||||||
|
"webSocketEvents": [
|
||||||
|
{
|
||||||
|
"event": "log:entry",
|
||||||
|
"count": 269,
|
||||||
|
"bytes": 62887
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "sensorFrame",
|
||||||
|
"count": 99,
|
||||||
|
"bytes": 185012
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "session:sync",
|
||||||
|
"count": 31,
|
||||||
|
"bytes": 526178
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "commandAck",
|
||||||
|
"count": 30,
|
||||||
|
"bytes": 2833
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "",
|
||||||
|
"count": 15,
|
||||||
|
"bytes": 10168579
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverSnapshot:frame",
|
||||||
|
"count": 11,
|
||||||
|
"bytes": 1067
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "mode",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 54
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "controlGranted",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 83
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "alert",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 257
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "alert:new",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "auth:role",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "rovers",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 10906
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "chat:init",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 155226
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "overseer:memory",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 1040
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "log:init",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 88662
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "barcode:state",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 3254
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "kinect:pointCloudFrame",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 524
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "kinect:colorFrame",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 338
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "2",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverSnapshot:status",
|
||||||
|
"count": 2,
|
||||||
|
"bytes": 159
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "0{\"sid\":\"v0f",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "40{\"sid\":\"5t",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "430[{\"succes",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 39
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "431[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 133
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "432[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "Freaky",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "434[{\"succes",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 194
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "0{\"sid\":\"t1y",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "40{\"sid\":\"Bu",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "436[{\"error\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "437[{\"error\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "438[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 135
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "439[{\"url\":\"",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 147
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "Bweeble",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 42
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "roverHostStats",
|
||||||
|
"count": 1,
|
||||||
|
"bytes": 771
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mutationsTotal": {
|
||||||
|
"records": 7648,
|
||||||
|
"added": 227,
|
||||||
|
"removed": 34,
|
||||||
|
"attrs": 7351,
|
||||||
|
"text": 36
|
||||||
|
},
|
||||||
|
"mutationTargets": [
|
||||||
|
{
|
||||||
|
"key": "input.accent-emerald-400:attributes:name",
|
||||||
|
"count": 2612
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-emerald-400:attributes:type",
|
||||||
|
"count": 1306
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.flex-1:attributes:name",
|
||||||
|
"count": 432
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:name",
|
||||||
|
"count": 432
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-cyan-500:attributes:name",
|
||||||
|
"count": 428
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:name",
|
||||||
|
"count": 330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.h-full:attributes:style",
|
||||||
|
"count": 278
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.chat-composer-input:attributes:name",
|
||||||
|
"count": 216
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input:attributes:name",
|
||||||
|
"count": 216
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:type",
|
||||||
|
"count": 216
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.accent-cyan-500:attributes:type",
|
||||||
|
"count": 214
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:type",
|
||||||
|
"count": 165
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:d",
|
||||||
|
"count": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:fill",
|
||||||
|
"count": 139
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input:attributes:type",
|
||||||
|
"count": 108
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Text:characterData:",
|
||||||
|
"count": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "section.panel-section.border:attributes:style",
|
||||||
|
"count": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "header.flex.items-center:attributes:style",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "path.[object.SVGAnimatedString]:attributes:stroke",
|
||||||
|
"count": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.min-w-0.flex-1:attributes:name",
|
||||||
|
"count": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:value",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "span.inline-flex.items-center:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.rounded-sm.px-2:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.flex:attributes:disabled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.min-h-9:attributes:disabled",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.fixed.bottom-2:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.mobile-touch-control.flex:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.mobile-touch-control.flex:attributes:class",
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:placeholder",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:playsinline",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:autoplay",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "video.h-full.w-full:attributes:disableremoteplayback",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "audio:attributes:autoplay",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "audio:attributes:disableremoteplayback",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.flex-1:attributes:value",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "span.inline-block.rounded:attributes:style",
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "div.flex.flex-col:attributes:class",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.button-dark.min-w-0:attributes:disabled",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "input.field-input.w-full:attributes:value",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "button.button-dark.text-sm:attributes:disabled",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mutationBursts": [
|
||||||
|
{
|
||||||
|
"at": 10155,
|
||||||
|
"records": 164,
|
||||||
|
"added": 105,
|
||||||
|
"removed": 1,
|
||||||
|
"attrs": 58,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 28081,
|
||||||
|
"records": 103,
|
||||||
|
"added": 7,
|
||||||
|
"removed": 12,
|
||||||
|
"attrs": 79,
|
||||||
|
"text": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 31972,
|
||||||
|
"records": 83,
|
||||||
|
"added": 13,
|
||||||
|
"removed": 3,
|
||||||
|
"attrs": 65,
|
||||||
|
"text": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 18426,
|
||||||
|
"records": 82,
|
||||||
|
"added": 11,
|
||||||
|
"removed": 2,
|
||||||
|
"attrs": 66,
|
||||||
|
"text": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 8027,
|
||||||
|
"records": 69,
|
||||||
|
"added": 19,
|
||||||
|
"removed": 5,
|
||||||
|
"attrs": 42,
|
||||||
|
"text": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 16714,
|
||||||
|
"records": 66,
|
||||||
|
"added": 2,
|
||||||
|
"removed": 2,
|
||||||
|
"attrs": 53,
|
||||||
|
"text": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 40645,
|
||||||
|
"records": 64,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 64,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 40951,
|
||||||
|
"records": 64,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 64,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 42708,
|
||||||
|
"records": 64,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 64,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 36974,
|
||||||
|
"records": 61,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 61,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 24109,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 28572,
|
||||||
|
"records": 60,
|
||||||
|
"added": 2,
|
||||||
|
"removed": 4,
|
||||||
|
"attrs": 53,
|
||||||
|
"text": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 36595,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 42135,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 43257,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 48207,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 48500,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 50368,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 51568,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"at": 52146,
|
||||||
|
"records": 60,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0,
|
||||||
|
"attrs": 60,
|
||||||
|
"text": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frames": {
|
||||||
|
"count": 241,
|
||||||
|
"average": 202.06639004149378,
|
||||||
|
"p95": 399.8999999999978,
|
||||||
|
"p99": 1633.199999999999
|
||||||
|
},
|
||||||
|
"longTasks": {
|
||||||
|
"count": 133,
|
||||||
|
"top": [
|
||||||
|
{
|
||||||
|
"startTime": 1510.7999999970198,
|
||||||
|
"duration": 2281,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 10156.89999999851,
|
||||||
|
"duration": 1493,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 3806,
|
||||||
|
"duration": 1311,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 30277.099999997765,
|
||||||
|
"duration": 932,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 14996,
|
||||||
|
"duration": 931,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 9269.89999999851,
|
||||||
|
"duration": 886,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 7147,
|
||||||
|
"duration": 880,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 14298,
|
||||||
|
"duration": 698,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 29581.099999997765,
|
||||||
|
"duration": 696,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 8029.599999997765,
|
||||||
|
"duration": 689,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 5117.39999999851,
|
||||||
|
"duration": 534,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 31587,
|
||||||
|
"duration": 385,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 18074,
|
||||||
|
"duration": 356,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 25434.39999999851,
|
||||||
|
"duration": 344,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 16382.29999999702,
|
||||||
|
"duration": 333,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 12463.199999999255,
|
||||||
|
"duration": 300,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 6124.79999999702,
|
||||||
|
"duration": 285,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 5657.199999999255,
|
||||||
|
"duration": 283,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 18655.79999999702,
|
||||||
|
"duration": 242,
|
||||||
|
"name": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": 19023.5,
|
||||||
|
"duration": 236,
|
||||||
|
"name": "self"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"consoleErrors": []
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
||||||
<title>Roomba Rover</title>
|
<title>Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-DhJId5qi.js"></script>
|
<script type="module" crossorigin src="/assets/index-D0FgC_A-.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BVKa8Zph.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BVKa8Zph.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+2
-3
@@ -11,7 +11,7 @@ import {
|
|||||||
ControlSystemProvider,
|
ControlSystemProvider,
|
||||||
KeyboardInputManager,
|
KeyboardInputManager,
|
||||||
GamepadInputManager,
|
GamepadInputManager,
|
||||||
useControlSystem,
|
useControlSelector,
|
||||||
} from './controls/index.js';
|
} from './controls/index.js';
|
||||||
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
|
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
|
||||||
import KinectPanel from './components/KinectPanel/index.jsx';
|
import KinectPanel from './components/KinectPanel/index.jsx';
|
||||||
@@ -105,10 +105,9 @@ function MobileFeatureTabs({
|
|||||||
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
||||||
return roverId ? state.session?.audioForward?.[roverId] || null : null;
|
return roverId ? state.session?.audioForward?.[roverId] || null : null;
|
||||||
});
|
});
|
||||||
const { state: controlState } = useControlSystem();
|
const pttActive = useControlSelector((control) => Boolean(control.state.mic?.pttActive));
|
||||||
const { value: vipAudio } = useSettingsNamespace('vipAudio', { openMicEnabled: false, pttMode: 'live' });
|
const { value: vipAudio } = useSettingsNamespace('vipAudio', { openMicEnabled: false, pttMode: 'live' });
|
||||||
const vipDotClass = isVerified ? 'bg-emerald-400' : 'bg-amber-400';
|
const vipDotClass = isVerified ? 'bg-emerald-400' : 'bg-amber-400';
|
||||||
const pttActive = Boolean(controlState?.mic?.pttActive);
|
|
||||||
const openMicEnabled = Boolean(vipAudio?.openMicEnabled);
|
const openMicEnabled = Boolean(vipAudio?.openMicEnabled);
|
||||||
const pttMode = vipAudio?.pttMode === 'clip' ? 'clip' : 'live';
|
const pttMode = vipAudio?.pttMode === 'clip' ? 'clip' : 'live';
|
||||||
const vipMicActive = Boolean(
|
const vipMicActive = Boolean(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Purpose: Defines the Drive Dock Action module and the local helpers/components used in this file.
|
// Purpose: Defines the Drive Dock Action module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||||
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||||
@@ -103,10 +103,9 @@ export default function DriveDockAction({
|
|||||||
compactHeightClass = '',
|
compactHeightClass = '',
|
||||||
}) {
|
}) {
|
||||||
const isMobile = layout === 'mobile';
|
const isMobile = layout === 'mobile';
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId, keymap },
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
actions,
|
const actions = useControlActions();
|
||||||
} = useControlSystem();
|
|
||||||
const dockAssist = useManualDockAssist();
|
const dockAssist = useManualDockAssist();
|
||||||
const frame = useTelemetryFrame(roverId);
|
const frame = useTelemetryFrame(roverId);
|
||||||
const state = driveDockState ?? deriveDriveDockState(frame);
|
const state = driveDockState ?? deriveDriveDockState(frame);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import RoverMediaPlayer from '../RoverMediaPlayer/index.jsx';
|
import RoverMediaPlayer from '../RoverMediaPlayer/index.jsx';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import { useDriverVideoModePolicy } from '../../hooks/useDriverVideoModePolicy.js';
|
import { useDriverVideoModePolicy } from '../../hooks/useDriverVideoModePolicy.js';
|
||||||
import TurnsOverlay from '../HudOverlays/TurnsOverlay/index.jsx';
|
import TurnsOverlay from '../HudOverlays/TurnsOverlay/index.jsx';
|
||||||
import HudOverlay from '../HudOverlays/HudOverlay/index.jsx';
|
import HudOverlay from '../HudOverlays/HudOverlay/index.jsx';
|
||||||
@@ -15,9 +15,7 @@ import CardFrame from '../CardFrame/index.jsx';
|
|||||||
export default function DriverVideo({ layoutFormat = 'desktop' }) {
|
export default function DriverVideo({ layoutFormat = 'desktop' }) {
|
||||||
const roverId = useSessionSelector((state) => state.session?.assignment?.roverId ?? null);
|
const roverId = useSessionSelector((state) => state.session?.assignment?.roverId ?? null);
|
||||||
const videoMode = useDriverVideoModePolicy(roverId);
|
const videoMode = useDriverVideoModePolicy(roverId);
|
||||||
const {
|
const lastControlIntentAt = useControlSelector((control) => control.state.lastControlIntentAt);
|
||||||
state: { lastControlIntentAt },
|
|
||||||
} = useControlSystem();
|
|
||||||
|
|
||||||
if (!roverId) {
|
if (!roverId) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Help Overlay
|
// Help Overlay
|
||||||
// Purpose: Defines the Help Overlay module and the local helpers/components used in this file.
|
// Purpose: Defines the Help Overlay module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import HelpContentView from '../HelpContentView/index.jsx';
|
import HelpContentView from '../HelpContentView/index.jsx';
|
||||||
|
|
||||||
export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onToggleShowOnLoad }) {
|
export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onToggleShowOnLoad }) {
|
||||||
if (!visible) return null;
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
|
|
||||||
const { state } = useControlSystem();
|
if (!visible) return null;
|
||||||
|
|
||||||
const handleCheckbox = (event) => {
|
const handleCheckbox = (event) => {
|
||||||
const keepShowing = !event.target.checked;
|
const keepShowing = !event.target.checked;
|
||||||
@@ -39,7 +39,7 @@ export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onTo
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-h-[85vh] overflow-y-auto p-0.5">
|
<div className="max-h-[85vh] overflow-y-auto p-0.5">
|
||||||
<HelpContentView layout={layout} keymap={state?.keymap || {}} />
|
<HelpContentView layout={layout} keymap={keymap || {}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Help Panel
|
// Help Panel
|
||||||
// Purpose: Defines the Help Panel module and the local helpers/components used in this file.
|
// Purpose: Defines the Help Panel module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
import HelpContentView from '../HelpContentView/index.jsx';
|
import HelpContentView from '../HelpContentView/index.jsx';
|
||||||
|
|
||||||
export default function HelpPanel({ layout, onOpenOverlay }) {
|
export default function HelpPanel({ layout, onOpenOverlay }) {
|
||||||
const { state } = useControlSystem();
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
|
|
||||||
const actions = (
|
const actions = (
|
||||||
<button
|
<button
|
||||||
@@ -20,7 +20,7 @@ export default function HelpPanel({ layout, onOpenOverlay }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<CardFrame title="Help" actions={actions} bodyClassName="space-y-0.5 text-sm">
|
<CardFrame title="Help" actions={actions} bodyClassName="space-y-0.5 text-sm">
|
||||||
<HelpContentView layout={layout} keymap={state?.keymap || {}} />
|
<HelpContentView layout={layout} keymap={keymap || {}} />
|
||||||
</CardFrame>
|
</CardFrame>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
|
|
||||||
@@ -169,9 +169,7 @@ function LampTile({ entity, connected, controlsLocked, onToggle, onSetColor, onS
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function HomeAssistantControls() {
|
export default function HomeAssistantControls() {
|
||||||
const {
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
state: { keymap },
|
|
||||||
} = useControlSystem();
|
|
||||||
const ha = useSessionSelector((state) => state.session?.homeAssistant || null);
|
const ha = useSessionSelector((state) => state.session?.homeAssistant || null);
|
||||||
const { homeAssistantToggle, homeAssistantSetLightColor, homeAssistantSetLightWhite } =
|
const { homeAssistantToggle, homeAssistantSetLightColor, homeAssistantSetLightWhite } =
|
||||||
useSessionActions();
|
useSessionActions();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useSessionSelector } from '../../../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../../context/SessionContext.jsx';
|
||||||
import { useControlSystem } from '../../../controls/index.js';
|
import { useControlSelector } from '../../../controls/index.js';
|
||||||
import SocialButton from '../../SocialButton/index.jsx';
|
import SocialButton from '../../SocialButton/index.jsx';
|
||||||
|
|
||||||
function TurnsOverlay({
|
function TurnsOverlay({
|
||||||
@@ -15,9 +15,7 @@ function TurnsOverlay({
|
|||||||
const turnQueues = useSessionSelector((state) => state.session?.turnQueues ?? {});
|
const turnQueues = useSessionSelector((state) => state.session?.turnQueues ?? {});
|
||||||
const socketId = useSessionSelector((state) => state.session?.socketId || null);
|
const socketId = useSessionSelector((state) => state.session?.socketId || null);
|
||||||
const activeDrivers = useSessionSelector((state) => state.session?.activeDrivers ?? {});
|
const activeDrivers = useSessionSelector((state) => state.session?.activeDrivers ?? {});
|
||||||
const {
|
const lastControlIntentAt = useControlSelector((control) => control.state.lastControlIntentAt);
|
||||||
state: { lastControlIntentAt },
|
|
||||||
} = useControlSystem();
|
|
||||||
const [now, setNow] = useState(() => Date.now());
|
const [now, setNow] = useState(() => Date.now());
|
||||||
const [showTurnCue, setShowTurnCue] = useState(false);
|
const [showTurnCue, setShowTurnCue] = useState(false);
|
||||||
const [turnCueStartAt, setTurnCueStartAt] = useState(null);
|
const [turnCueStartAt, setTurnCueStartAt] = useState(null);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Purpose: Defines the Keymap Settings module and the local helpers/components used in this file.
|
// Purpose: Defines the Keymap Settings module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useMemo, useState, useEffect, useCallback } from 'react';
|
import { useMemo, useState, useEffect, useCallback } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { DEFAULT_KEYMAP } from '../../controls/constants.js';
|
import { DEFAULT_KEYMAP } from '../../controls/constants.js';
|
||||||
import { canonicalizeKeyInput, formatKeyLabel } from '../../controls/keymapUtils.js';
|
import { canonicalizeKeyInput, formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import { setKeyboardCaptureLocked } from '../../controls/inputs/keyboardCaptureLock.js';
|
import { setKeyboardCaptureLocked } from '../../controls/inputs/keyboardCaptureLock.js';
|
||||||
@@ -191,10 +191,8 @@ function BindingRow({ action, value, isActive, onCaptureStart, onCancel, onCaptu
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function KeymapSettings() {
|
export default function KeymapSettings() {
|
||||||
const {
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
state: { keymap },
|
const { updateKeyBinding, resetKeyBindings } = useControlActions();
|
||||||
actions: { updateKeyBinding, resetKeyBindings },
|
|
||||||
} = useControlSystem();
|
|
||||||
const { value: inputSettings, save: saveInputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
const { value: inputSettings, save: saveInputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
||||||
const keyboardSettings = {
|
const keyboardSettings = {
|
||||||
...INPUT_SETTINGS_DEFAULTS.keyboard,
|
...INPUT_SETTINGS_DEFAULTS.keyboard,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Purpose: Assembles the mobile auxiliary controls column, which is the left column by default.
|
// Purpose: Assembles the mobile auxiliary controls column, which is the left column by default.
|
||||||
// Scope: Owns mobile aux/camera/night vision/horn wiring while reusing desktop variation components where intended.
|
// Scope: Owns mobile aux/camera/night vision/horn wiring while reusing desktop variation components where intended.
|
||||||
import { useCallback, useRef } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||||
import HornControl from '../HornControl/index.jsx';
|
import HornControl from '../HornControl/index.jsx';
|
||||||
import NightVisionControl from '../NightVisionControl/index.jsx';
|
import NightVisionControl from '../NightVisionControl/index.jsx';
|
||||||
@@ -11,19 +11,20 @@ import VacuumControls from './VacuumControls.jsx';
|
|||||||
import VerticalCameraTilt from './VerticalCameraTilt.jsx';
|
import VerticalCameraTilt from './VerticalCameraTilt.jsx';
|
||||||
|
|
||||||
function AuxColumnContent() {
|
function AuxColumnContent() {
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId, camera, horn },
|
const camera = useControlSelector((control) => control.state.camera);
|
||||||
pipeline,
|
const horn = useControlSelector((control) => control.state.horn);
|
||||||
actions: { setServoAngle, setNightVision, setAuxMotors, startHorn, stopHorn },
|
const nightVision = useControlSelector((control) => control.pipeline?.nightVision);
|
||||||
} = useControlSystem();
|
const nightVisionState = useControlSelector((control) => control.pipeline?.nightVisionState);
|
||||||
|
const pipelineHorn = useControlSelector((control) => control.pipeline?.horn);
|
||||||
|
const { setServoAngle, setNightVision, setAuxMotors, startHorn, stopHorn } = useControlActions();
|
||||||
const dockAssist = useManualDockAssist();
|
const dockAssist = useManualDockAssist();
|
||||||
const disabled = !roverId;
|
const disabled = !roverId;
|
||||||
const activeAuxButtonRef = useRef(null);
|
const activeAuxButtonRef = useRef(null);
|
||||||
const cameraConfig = camera?.config;
|
const cameraConfig = camera?.config;
|
||||||
const cameraEnabled = Boolean(roverId && camera?.enabled && cameraConfig);
|
const cameraEnabled = Boolean(roverId && camera?.enabled && cameraConfig);
|
||||||
const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision);
|
const nightVisionAvailable = Boolean(roverId && nightVision);
|
||||||
const nightVisionState = pipeline?.nightVisionState;
|
const hornAvailable = Boolean(roverId && pipelineHorn);
|
||||||
const hornAvailable = Boolean(roverId && pipeline?.horn);
|
|
||||||
const hornBlocked = horn?.overheated;
|
const hornBlocked = horn?.overheated;
|
||||||
const cameraMin = typeof cameraConfig?.minAngle === 'number' ? cameraConfig.minAngle : -45;
|
const cameraMin = typeof cameraConfig?.minAngle === 'number' ? cameraConfig.minAngle : -45;
|
||||||
const cameraMax = typeof cameraConfig?.maxAngle === 'number' ? cameraConfig.maxAngle : 45;
|
const cameraMax = typeof cameraConfig?.maxAngle === 'number' ? cameraConfig.maxAngle : 45;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Purpose: Provides the mobile movement control pad and speed mode selector.
|
// Purpose: Provides the mobile movement control pad and speed mode selector.
|
||||||
// Scope: Converts touch pad cells into keyboard-style drive vectors; movement column owns drive/dock placement.
|
// Scope: Converts touch pad cells into keyboard-style drive vectors; movement column owns drive/dock placement.
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { normalizeKeymapEntries } from '../../controls/keymapUtils.js';
|
import { normalizeKeymapEntries } from '../../controls/keymapUtils.js';
|
||||||
import {
|
import {
|
||||||
computeKeyboardDriveVector,
|
computeKeyboardDriveVector,
|
||||||
@@ -29,10 +29,8 @@ function getSpeedModeConfig(speedMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ControlPadPanel({ disabled = false }) {
|
export default function ControlPadPanel({ disabled = false }) {
|
||||||
const {
|
const rawKeymap = useControlSelector((control) => control.state.keymap);
|
||||||
state: { keymap: rawKeymap },
|
const { setDriveVector, registerInputState } = useControlActions();
|
||||||
actions: { setDriveVector, registerInputState },
|
|
||||||
} = useControlSystem();
|
|
||||||
const { value: inputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
const { value: inputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
||||||
const [speedMode, setSpeedMode] = useState('normal');
|
const [speedMode, setSpeedMode] = useState('normal');
|
||||||
const [activeInputLabel, setActiveInputLabel] = useState('stop');
|
const [activeInputLabel, setActiveInputLabel] = useState('stop');
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
// Movement Column
|
// Movement Column
|
||||||
// Purpose: Assembles the mobile movement column, which is the right column by default.
|
// Purpose: Assembles the mobile movement column, which is the right column by default.
|
||||||
// Scope: Integrates drive/dock actions with the mobile control pad without hiding that dependency inside the pad.
|
// Scope: Integrates drive/dock actions with the mobile control pad without hiding that dependency inside the pad.
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
||||||
import ControlPadPanel from './ControlPadPanel.jsx';
|
import ControlPadPanel from './ControlPadPanel.jsx';
|
||||||
|
|
||||||
function MovementColumnContent({ layout }) {
|
function MovementColumnContent({ layout }) {
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId },
|
|
||||||
} = useControlSystem();
|
|
||||||
const driveDockState = useDriveDockState(roverId);
|
const driveDockState = useDriveDockState(roverId);
|
||||||
const dockedNotDriving = driveDockState.docked && !driveDockState.driving;
|
const dockedNotDriving = driveDockState.docked && !driveDockState.driving;
|
||||||
const expandAction = dockedNotDriving || driveDockState.dockingInProgress;
|
const expandAction = dockedNotDriving || driveDockState.dockingInProgress;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Purpose: Defines the Overcurrent Limiter Panel module and the local helpers/components used in this file.
|
// Purpose: Defines the Overcurrent Limiter Panel module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import { OVERCURRENT_GROUPS } from '../../controls/overcurrentLimiter.js';
|
import { OVERCURRENT_GROUPS } from '../../controls/overcurrentLimiter.js';
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
|
|
||||||
@@ -26,10 +26,8 @@ function ProgressBar({ value, color = 'bg-emerald-500' }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function OvercurrentLimiterPanel() {
|
export default function OvercurrentLimiterPanel() {
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId },
|
const overcurrentLimiter = useControlSelector((control) => control.overcurrentLimiter);
|
||||||
overcurrentLimiter,
|
|
||||||
} = useControlSystem();
|
|
||||||
const groups = useMemo(() => OVERCURRENT_GROUPS.map((group) => group.key), []);
|
const groups = useMemo(() => OVERCURRENT_GROUPS.map((group) => group.key), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import NicknameForm from '../NicknameForm/index.jsx';
|
import NicknameForm from '../NicknameForm/index.jsx';
|
||||||
import SocialButton from '../SocialButton/index.jsx';
|
import SocialButton from '../SocialButton/index.jsx';
|
||||||
@@ -53,9 +53,9 @@ export default function QuickstartOverlay({
|
|||||||
onOpenHelp,
|
onOpenHelp,
|
||||||
onClose,
|
onClose,
|
||||||
}) {
|
}) {
|
||||||
const { state } = useControlSystem();
|
const rawKeymap = useControlSelector((control) => control.state.keymap);
|
||||||
const isDesktop = layout === 'desktop';
|
const isDesktop = layout === 'desktop';
|
||||||
const keymap = useMemo(() => state?.keymap || {}, [state?.keymap]);
|
const keymap = useMemo(() => rawKeymap || {}, [rawKeymap]);
|
||||||
|
|
||||||
if (!visible) return null;
|
if (!visible) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx';
|
|||||||
import TopDownMap from '../TopDownMap/index.jsx';
|
import TopDownMap from '../TopDownMap/index.jsx';
|
||||||
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
||||||
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import RoverQueuesPanel from '../RoverQueuesPanel/index.jsx';
|
import RoverQueuesPanel from '../RoverQueuesPanel/index.jsx';
|
||||||
import RawUserPilePanel from '../RawUserPilePanel/index.jsx';
|
import RawUserPilePanel from '../RawUserPilePanel/index.jsx';
|
||||||
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
@@ -36,9 +36,7 @@ const CHAT_DOCK_MAX_HEIGHT = 300;
|
|||||||
const CHAT_DOCK_BOTTOM_INSET = 8;
|
const CHAT_DOCK_BOTTOM_INSET = 8;
|
||||||
|
|
||||||
function TopDownMapPanel() {
|
function TopDownMapPanel() {
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId },
|
|
||||||
} = useControlSystem();
|
|
||||||
const frame = useTelemetryFrame(roverId);
|
const frame = useTelemetryFrame(roverId);
|
||||||
const sensors = frame?.sensors || {};
|
const sensors = frame?.sensors || {};
|
||||||
|
|
||||||
@@ -54,20 +52,22 @@ function TopDownMapPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DriveDockPanel() {
|
function DriveDockPanel() {
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId, keymap, camera, horn },
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
pipeline,
|
const camera = useControlSelector((control) => control.state.camera);
|
||||||
actions: { setServoAngle, setNightVision, startHorn, stopHorn },
|
const horn = useControlSelector((control) => control.state.horn);
|
||||||
} = useControlSystem();
|
const nightVision = useControlSelector((control) => control.pipeline?.nightVision);
|
||||||
|
const nightVisionState = useControlSelector((control) => control.pipeline?.nightVisionState);
|
||||||
|
const pipelineHorn = useControlSelector((control) => control.pipeline?.horn);
|
||||||
|
const { setServoAngle, setNightVision, startHorn, stopHorn } = useControlActions();
|
||||||
const dockAssist = useManualDockAssist();
|
const dockAssist = useManualDockAssist();
|
||||||
const driveDockState = useDriveDockState(roverId);
|
const driveDockState = useDriveDockState(roverId);
|
||||||
const hideInlineControls = driveDockState.docked && !driveDockState.driving;
|
const hideInlineControls = driveDockState.docked && !driveDockState.driving;
|
||||||
|
|
||||||
const config = camera?.config;
|
const config = camera?.config;
|
||||||
const cameraEnabled = Boolean(roverId && camera?.enabled && config);
|
const cameraEnabled = Boolean(roverId && camera?.enabled && config);
|
||||||
const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision);
|
const nightVisionAvailable = Boolean(roverId && nightVision);
|
||||||
const nightVisionState = pipeline?.nightVisionState;
|
const hornAvailable = Boolean(roverId && pipelineHorn);
|
||||||
const hornAvailable = Boolean(roverId && pipeline?.horn);
|
|
||||||
const hornBlocked = horn?.overheated;
|
const hornBlocked = horn?.overheated;
|
||||||
const min = typeof config?.minAngle === 'number' ? config.minAngle : -30;
|
const min = typeof config?.minAngle === 'number' ? config.minAngle : -30;
|
||||||
const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30;
|
const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30;
|
||||||
@@ -146,10 +146,9 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
|||||||
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
||||||
return roverId ? state.session?.audioForward?.[roverId] || null : null;
|
return roverId ? state.session?.audioForward?.[roverId] || null : null;
|
||||||
});
|
});
|
||||||
const { state: controlState } = useControlSystem();
|
const pttActive = useControlSelector((control) => Boolean(control.state.mic?.pttActive));
|
||||||
const { value: vipAudio } = useSettingsNamespace('vipAudio', { openMicEnabled: false, pttMode: 'live' });
|
const { value: vipAudio } = useSettingsNamespace('vipAudio', { openMicEnabled: false, pttMode: 'live' });
|
||||||
const vipDotClass = isVerified ? 'bg-emerald-400' : 'bg-red-600';
|
const vipDotClass = isVerified ? 'bg-emerald-400' : 'bg-red-600';
|
||||||
const pttActive = Boolean(controlState?.mic?.pttActive);
|
|
||||||
const openMicEnabled = Boolean(vipAudio?.openMicEnabled);
|
const openMicEnabled = Boolean(vipAudio?.openMicEnabled);
|
||||||
const pttMode = vipAudio?.pttMode === 'clip' ? 'clip' : 'live';
|
const pttMode = vipAudio?.pttMode === 'clip' ? 'clip' : 'live';
|
||||||
const vipMicActive = Boolean(
|
const vipMicActive = Boolean(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Purpose: Defines the Settings Panel module and the local helpers/components used in this file.
|
// Purpose: Defines the Settings Panel module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import AuthPanel from '../AuthPanel/index.jsx';
|
import AuthPanel from '../AuthPanel/index.jsx';
|
||||||
import AdminPanel from '../AdminPanel/index.jsx';
|
import AdminPanel from '../AdminPanel/index.jsx';
|
||||||
import KeymapSettings from '../KeymapSettings/index.jsx';
|
import KeymapSettings from '../KeymapSettings/index.jsx';
|
||||||
@@ -91,10 +91,9 @@ function RangeSetting({ label, value, disabled = false, onChange }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SettingsPanel() {
|
export default function SettingsPanel() {
|
||||||
const {
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
state: { keymap, roverId },
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
actions: { sendOiCommand, setSensorStream },
|
const { sendOiCommand, setSensorStream } = useControlActions();
|
||||||
} = useControlSystem();
|
|
||||||
const canControl = Boolean(roverId);
|
const canControl = Boolean(roverId);
|
||||||
const [hudMapDesktop, setHudMapDesktop] = useHudMapSetting();
|
const [hudMapDesktop, setHudMapDesktop] = useHudMapSetting();
|
||||||
const socket = useSocket();
|
const socket = useSocket();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { fieldClass } from '../constants.js';
|
import { fieldClass } from '../constants.js';
|
||||||
import { useControlSystem } from '../../../controls/index.js';
|
import { useControlSelector } from '../../../controls/index.js';
|
||||||
import { formatKeyLabel } from '../../../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../../controls/keymapUtils.js';
|
||||||
import { useSettingsNamespace } from '../../../settings/index.js';
|
import { useSettingsNamespace } from '../../../settings/index.js';
|
||||||
import { MAX_UPLOAD_BYTES, TARGET_SAMPLE_RATE, RTC_CONFIG } from './constants.js';
|
import { MAX_UPLOAD_BYTES, TARGET_SAMPLE_RATE, RTC_CONFIG } from './constants.js';
|
||||||
@@ -28,7 +28,8 @@ export default function VipAudioUploadCard({
|
|||||||
readyMicWhip,
|
readyMicWhip,
|
||||||
stopMicWhip,
|
stopMicWhip,
|
||||||
}) {
|
}) {
|
||||||
const { state: controlState } = useControlSystem();
|
const keymap = useControlSelector((control) => control.state.keymap);
|
||||||
|
const pttActive = useControlSelector((control) => Boolean(control.state.mic?.pttActive));
|
||||||
const { value: vipAudio, save: saveVipAudio } = useSettingsNamespace('vipAudio', {
|
const { value: vipAudio, save: saveVipAudio } = useSettingsNamespace('vipAudio', {
|
||||||
openMicEnabled: false,
|
openMicEnabled: false,
|
||||||
pttMode: 'live',
|
pttMode: 'live',
|
||||||
@@ -60,7 +61,6 @@ export default function VipAudioUploadCard({
|
|||||||
const clipRecordingRef = useRef(false);
|
const clipRecordingRef = useRef(false);
|
||||||
const clipSampleRateRef = useRef(TARGET_SAMPLE_RATE);
|
const clipSampleRateRef = useRef(TARGET_SAMPLE_RATE);
|
||||||
|
|
||||||
const pttActive = Boolean(controlState?.mic?.pttActive);
|
|
||||||
|
|
||||||
const selectedForwardState = useMemo(
|
const selectedForwardState = useMemo(
|
||||||
() => (roverId ? audioForwardByRover?.[roverId] || null : null),
|
() => (roverId ? audioForwardByRover?.[roverId] || null : null),
|
||||||
@@ -81,7 +81,7 @@ export default function VipAudioUploadCard({
|
|||||||
const whipLinkActive = !clipMode && (micState === 'live' || micState === 'starting');
|
const whipLinkActive = !clipMode && (micState === 'live' || micState === 'starting');
|
||||||
const clipRecording = clipMode && clipState === 'recording';
|
const clipRecording = clipMode && clipState === 'recording';
|
||||||
const clipSending = clipMode && clipState === 'sending';
|
const clipSending = clipMode && clipState === 'sending';
|
||||||
const pttKeyLabel = formatKeyLabel(controlState?.keymap?.micPtt?.[0]) || 'M';
|
const pttKeyLabel = formatKeyLabel(keymap?.micPtt?.[0]) || 'M';
|
||||||
|
|
||||||
const setPttMode = useCallback(
|
const setPttMode = useCallback(
|
||||||
(nextMode) => {
|
(nextMode) => {
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
// Control Context Provider
|
// Control Context Provider
|
||||||
// Purpose: Exposes control-system state/actions to control-capable components. Scope: Owns reducer wiring, pipeline integration, and top-level provider hooks.
|
// Purpose: Exposes control-system state/actions to control-capable components. Scope: Owns reducer wiring, pipeline integration, and top-level provider hooks.
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef } from 'react';
|
/* eslint-disable react-refresh/only-export-components */
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useMemo,
|
||||||
|
useReducer,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { controlReducer, initialControlState } from './controlReducer.js';
|
import { controlReducer, initialControlState } from './controlReducer.js';
|
||||||
import { computeDifferentialSpeeds, clamp } from './controlMath.js';
|
import { computeDifferentialSpeeds, clamp } from './controlMath.js';
|
||||||
import { useCommandPipeline } from './commandPipeline.js';
|
import { useCommandPipeline } from './commandPipeline.js';
|
||||||
@@ -27,6 +38,35 @@ import {
|
|||||||
|
|
||||||
const ControlSystemContext = createContext(null);
|
const ControlSystemContext = createContext(null);
|
||||||
|
|
||||||
|
function normalizeSelector(selector) {
|
||||||
|
return typeof selector === 'function' ? selector : (snapshot) => snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTROL_ACTION_NAMES = [
|
||||||
|
'setMode',
|
||||||
|
'setDriveVector',
|
||||||
|
'setAuxMotors',
|
||||||
|
'setServoAngle',
|
||||||
|
'nudgeServo',
|
||||||
|
'goServoHome',
|
||||||
|
'runMacro',
|
||||||
|
'stopAllMotion',
|
||||||
|
'sendOiCommand',
|
||||||
|
'setSensorStream',
|
||||||
|
'setNightVision',
|
||||||
|
'toggleNightVision',
|
||||||
|
'updateKeyBinding',
|
||||||
|
'resetKeyBindings',
|
||||||
|
'registerInputState',
|
||||||
|
'setManualDockAssistActive',
|
||||||
|
'toggleManualDockAssist',
|
||||||
|
'setSongNote',
|
||||||
|
'sendSong',
|
||||||
|
'startHorn',
|
||||||
|
'stopHorn',
|
||||||
|
'setMicPttActive',
|
||||||
|
];
|
||||||
|
|
||||||
function cloneKeymap(map) {
|
function cloneKeymap(map) {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(map || {}).map(([key, values]) => [key, Array.isArray(values) ? [...values] : []]),
|
Object.entries(map || {}).map(([key, values]) => [key, Array.isArray(values) ? [...values] : []]),
|
||||||
@@ -59,6 +99,53 @@ function removeDriveSequenceBackoff(steps = []) {
|
|||||||
|
|
||||||
export function ControlSystemProvider({ children }) {
|
export function ControlSystemProvider({ children }) {
|
||||||
const [state, dispatch] = useReducer(controlReducer, initialControlState);
|
const [state, dispatch] = useReducer(controlReducer, initialControlState);
|
||||||
|
const snapshotRef = useRef(null);
|
||||||
|
const subscribersRef = useRef(new Set());
|
||||||
|
const actionImplementationsRef = useRef({});
|
||||||
|
const [stableActions] = useState(() => {
|
||||||
|
/*
|
||||||
|
Public action functions intentionally keep stable identities. Each wrapper
|
||||||
|
reads the current implementation at call time, so consumers can depend on
|
||||||
|
actions without re-rendering just because the real implementation now
|
||||||
|
closes over a new state slice, pipeline object, or settings value.
|
||||||
|
*/
|
||||||
|
return Object.fromEntries(
|
||||||
|
CONTROL_ACTION_NAMES.map((name) => [
|
||||||
|
name,
|
||||||
|
(...args) => actionImplementationsRef.current[name]?.(...args),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
const getControlSnapshot = useCallback(() => snapshotRef.current, []);
|
||||||
|
const subscribeControlSnapshot = useCallback((selector, listener, equalityFn = Object.is) => {
|
||||||
|
const normalizedSelector = normalizeSelector(selector);
|
||||||
|
const currentSnapshot = snapshotRef.current;
|
||||||
|
const subscriber = {
|
||||||
|
selector: normalizedSelector,
|
||||||
|
equalityFn,
|
||||||
|
listener,
|
||||||
|
current: normalizedSelector(currentSnapshot),
|
||||||
|
};
|
||||||
|
subscribersRef.current.add(subscriber);
|
||||||
|
return () => {
|
||||||
|
subscribersRef.current.delete(subscriber);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
const notifyControlSubscribers = useCallback((nextSnapshot) => {
|
||||||
|
/*
|
||||||
|
The provider context value below is stable, so React will not fan out an
|
||||||
|
update to every consumer automatically. This loop is the replacement: it
|
||||||
|
asks each subscribed component whether the exact value it selected changed
|
||||||
|
and only wakes that component when its selected value is different.
|
||||||
|
*/
|
||||||
|
subscribersRef.current.forEach((subscriber) => {
|
||||||
|
const nextSelected = subscriber.selector(nextSnapshot);
|
||||||
|
if (!subscriber.equalityFn(subscriber.current, nextSelected)) {
|
||||||
|
subscriber.current = nextSelected;
|
||||||
|
subscriber.listener(nextSelected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
const prevModeRef = useRef(null);
|
const prevModeRef = useRef(null);
|
||||||
const pendingLightsRef = useRef(false);
|
const pendingLightsRef = useRef(false);
|
||||||
const servoAngleRef = useRef(initialControlState.camera.angle);
|
const servoAngleRef = useRef(initialControlState.camera.angle);
|
||||||
@@ -166,7 +253,7 @@ export function ControlSystemProvider({ children }) {
|
|||||||
if (pipeline.roverId) {
|
if (pipeline.roverId) {
|
||||||
pipeline.enableSensorStream();
|
pipeline.enableSensorStream();
|
||||||
}
|
}
|
||||||
}, [pipeline.roverId, pipeline.enableSensorStream]);
|
}, [pipeline]);
|
||||||
|
|
||||||
const setMode = useCallback(
|
const setMode = useCallback(
|
||||||
(mode) => {
|
(mode) => {
|
||||||
@@ -430,6 +517,8 @@ export function ControlSystemProvider({ children }) {
|
|||||||
return { waveform, freqs: normalized };
|
return { waveform, freqs: normalized };
|
||||||
}, [hornSettings]);
|
}, [hornSettings]);
|
||||||
|
|
||||||
|
const stopHornRef = useRef(null);
|
||||||
|
|
||||||
const startHorn = useCallback(() => {
|
const startHorn = useCallback(() => {
|
||||||
if (!pipeline.horn) return;
|
if (!pipeline.horn) return;
|
||||||
if (state.horn?.overheated) return false;
|
if (state.horn?.overheated) return false;
|
||||||
@@ -445,7 +534,7 @@ export function ControlSystemProvider({ children }) {
|
|||||||
hornAutoStopRef.current = null;
|
hornAutoStopRef.current = null;
|
||||||
}
|
}
|
||||||
hornAutoStopRef.current = setTimeout(() => {
|
hornAutoStopRef.current = setTimeout(() => {
|
||||||
stopHorn();
|
stopHornRef.current?.();
|
||||||
}, HORN_MAX_MS);
|
}, HORN_MAX_MS);
|
||||||
recordControlIntent();
|
recordControlIntent();
|
||||||
return true;
|
return true;
|
||||||
@@ -474,15 +563,13 @@ export function ControlSystemProvider({ children }) {
|
|||||||
}
|
}
|
||||||
dispatch({ type: 'control/set-horn-heat', payload: { heat: nextHeat, overheated: nextOverheated } });
|
dispatch({ type: 'control/set-horn-heat', payload: { heat: nextHeat, overheated: nextOverheated } });
|
||||||
}
|
}
|
||||||
}, [dispatch, pipeline, state.horn?.heat, state.horn?.overheated, HORN_HEAT_UP_PER_SEC]);
|
}, [dispatch, pipeline, state.horn?.heat, state.horn?.overheated]);
|
||||||
|
|
||||||
const hornStateRef = useRef({
|
const hornStateRef = useRef({
|
||||||
active: Boolean(state.horn?.active),
|
active: Boolean(state.horn?.active),
|
||||||
heat: state.horn?.heat ?? 0,
|
heat: state.horn?.heat ?? 0,
|
||||||
overheated: Boolean(state.horn?.overheated),
|
overheated: Boolean(state.horn?.overheated),
|
||||||
});
|
});
|
||||||
const stopHornRef = useRef(stopHorn);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
hornStateRef.current = {
|
hornStateRef.current = {
|
||||||
active: Boolean(state.horn?.active),
|
active: Boolean(state.horn?.active),
|
||||||
@@ -525,7 +612,7 @@ export function ControlSystemProvider({ children }) {
|
|||||||
}
|
}
|
||||||
}, tickMs);
|
}, tickMs);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [dispatch, hornNeedsTick, HORN_HEAT_COOL_PER_SEC, HORN_HEAT_RESUME_THRESHOLD, HORN_HEAT_UP_PER_SEC]);
|
}, [dispatch, hornNeedsTick]);
|
||||||
|
|
||||||
const setManualDockAssistActive = useCallback(
|
const setManualDockAssistActive = useCallback(
|
||||||
(active) => {
|
(active) => {
|
||||||
@@ -561,13 +648,8 @@ export function ControlSystemProvider({ children }) {
|
|||||||
dispatch({ type: 'control/set-mic-ptt', payload: Boolean(active) });
|
dispatch({ type: 'control/set-mic-ptt', payload: Boolean(active) });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const contextValue = useMemo(
|
const actionImplementations = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
state,
|
|
||||||
dispatch,
|
|
||||||
pipeline,
|
|
||||||
overcurrentLimiter,
|
|
||||||
actions: {
|
|
||||||
setMode,
|
setMode,
|
||||||
setDriveVector,
|
setDriveVector,
|
||||||
setAuxMotors,
|
setAuxMotors,
|
||||||
@@ -590,12 +672,8 @@ export function ControlSystemProvider({ children }) {
|
|||||||
startHorn,
|
startHorn,
|
||||||
stopHorn,
|
stopHorn,
|
||||||
setMicPttActive,
|
setMicPttActive,
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
state,
|
|
||||||
pipeline,
|
|
||||||
overcurrentLimiter,
|
|
||||||
setMode,
|
setMode,
|
||||||
setDriveVector,
|
setDriveVector,
|
||||||
setAuxMotors,
|
setAuxMotors,
|
||||||
@@ -621,13 +699,106 @@ export function ControlSystemProvider({ children }) {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <ControlSystemContext.Provider value={contextValue}>{children}</ControlSystemContext.Provider>;
|
const snapshot = useMemo(
|
||||||
|
() => ({
|
||||||
|
state,
|
||||||
|
dispatch,
|
||||||
|
pipeline,
|
||||||
|
overcurrentLimiter,
|
||||||
|
actions: stableActions,
|
||||||
|
}),
|
||||||
|
[state, pipeline, overcurrentLimiter, stableActions],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (snapshotRef.current == null) {
|
||||||
|
/*
|
||||||
|
The first render has to make a snapshot available synchronously because
|
||||||
|
descendants can call selector hooks during that same render pass. Later
|
||||||
|
renders publish updates from the layout effect below, after React has
|
||||||
|
committed the provider's new state.
|
||||||
|
*/
|
||||||
|
snapshotRef.current = snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
actionImplementationsRef.current = actionImplementations;
|
||||||
|
}, [actionImplementations]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (snapshotRef.current === snapshot) return;
|
||||||
|
snapshotRef.current = snapshot;
|
||||||
|
notifyControlSubscribers(snapshot);
|
||||||
|
}, [notifyControlSubscribers, snapshot]);
|
||||||
|
|
||||||
|
const store = useMemo(
|
||||||
|
() => ({
|
||||||
|
getSnapshot: getControlSnapshot,
|
||||||
|
subscribe: subscribeControlSnapshot,
|
||||||
|
actions: stableActions,
|
||||||
|
}),
|
||||||
|
[getControlSnapshot, stableActions, subscribeControlSnapshot],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <ControlSystemContext.Provider value={store}>{children}</ControlSystemContext.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useControlSelector(selector, equalityFn = Object.is) {
|
||||||
|
const store = useContext(ControlSystemContext);
|
||||||
|
if (!store) {
|
||||||
|
throw new Error('useControlSelector must be used within ControlSystemProvider');
|
||||||
|
}
|
||||||
|
const selectorRef = useRef(selector);
|
||||||
|
const equalityRef = useRef(equalityFn);
|
||||||
|
|
||||||
|
const [selected, setSelected] = useState(() => selector(store.getSnapshot()));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
/*
|
||||||
|
Selector functions are often declared inline at the call site. Refreshing
|
||||||
|
these refs from an effect keeps the subscription callback pointed at the
|
||||||
|
newest selector/equality pair without making render mutate refs.
|
||||||
|
*/
|
||||||
|
selectorRef.current = selector;
|
||||||
|
equalityRef.current = equalityFn;
|
||||||
|
}, [equalityFn, selector]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
/*
|
||||||
|
Recheck once after subscribing because the provider may have published a
|
||||||
|
newer snapshot between this component's render and its effect. The equality
|
||||||
|
guard keeps that synchronization from causing a redundant render.
|
||||||
|
*/
|
||||||
|
setSelected((prev) => {
|
||||||
|
const next = selectorRef.current(store.getSnapshot());
|
||||||
|
return equalityRef.current(prev, next) ? prev : next;
|
||||||
|
});
|
||||||
|
|
||||||
|
return store.subscribe(
|
||||||
|
(snapshot) => selectorRef.current(snapshot),
|
||||||
|
(nextSelected) => {
|
||||||
|
setSelected((prev) => (equalityRef.current(prev, nextSelected) ? prev : nextSelected));
|
||||||
|
},
|
||||||
|
(a, b) => equalityRef.current(a, b),
|
||||||
|
);
|
||||||
|
}, [store]);
|
||||||
|
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useControlActions() {
|
||||||
|
const store = useContext(ControlSystemContext);
|
||||||
|
if (!store) {
|
||||||
|
throw new Error('useControlActions must be used within ControlSystemProvider');
|
||||||
|
}
|
||||||
|
return store.actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useControlSystem() {
|
export function useControlSystem() {
|
||||||
const context = useContext(ControlSystemContext);
|
/*
|
||||||
if (!context) {
|
This compatibility hook intentionally selects the whole snapshot, so it has
|
||||||
throw new Error('useControlSystem must be used within ControlSystemProvider');
|
the same broad update behavior as the old context API. New and migrated
|
||||||
}
|
consumers should prefer useControlSelector/useControlActions so they only
|
||||||
return context;
|
re-render for data they actually read.
|
||||||
|
*/
|
||||||
|
return useControlSelector((snapshot) => snapshot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
// Control Module Exports
|
// Control Module Exports
|
||||||
// Purpose: Re-exports control context/providers and input managers from one entrypoint. Scope: Keeps control imports stable and concise for app/module consumers.
|
// Purpose: Re-exports control context/providers and input managers from one entrypoint. Scope: Keeps control imports stable and concise for app/module consumers.
|
||||||
export { ControlSystemProvider, useControlSystem } from './ControlContext.jsx';
|
export {
|
||||||
|
ControlSystemProvider,
|
||||||
|
useControlActions,
|
||||||
|
useControlSelector,
|
||||||
|
useControlSystem,
|
||||||
|
} from './ControlContext.jsx';
|
||||||
export { default as KeyboardInputManager } from './inputs/KeyboardInputManager.jsx';
|
export { default as KeyboardInputManager } from './inputs/KeyboardInputManager.jsx';
|
||||||
export { default as GamepadInputManager } from './inputs/GamepadInputManager.jsx';
|
export { default as GamepadInputManager } from './inputs/GamepadInputManager.jsx';
|
||||||
export { useOvercurrentLimiter } from './overcurrentLimiter.js';
|
export { useOvercurrentLimiter } from './overcurrentLimiter.js';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Gamepad Input Manager
|
// Gamepad Input Manager
|
||||||
// Purpose: Converts polled gamepad state into normalized control actions/commands. Scope: Integrates bindings, deadzone math, and dispatch callbacks for driving.
|
// Purpose: Converts polled gamepad state into normalized control actions/commands. Scope: Integrates bindings, deadzone math, and dispatch callbacks for driving.
|
||||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
||||||
import { useControlSystem } from '../ControlContext.jsx';
|
import { useControlActions, useControlSelector } from '../ControlContext.jsx';
|
||||||
import { useSettingsNamespace } from '../../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { GAMEPAD_SETTINGS_DEFAULTS, GAMEPAD_PROFILE_DEFAULT } from '../../settings/namespaces.js';
|
import { GAMEPAD_SETTINGS_DEFAULTS, GAMEPAD_PROFILE_DEFAULT } from '../../settings/namespaces.js';
|
||||||
import {
|
import {
|
||||||
@@ -48,8 +48,6 @@ function pickActivePad(pads, activeSignature) {
|
|||||||
|
|
||||||
export default function GamepadInputManager() {
|
export default function GamepadInputManager() {
|
||||||
const {
|
const {
|
||||||
state,
|
|
||||||
actions: {
|
|
||||||
setMode,
|
setMode,
|
||||||
setDriveVector,
|
setDriveVector,
|
||||||
setAuxMotors,
|
setAuxMotors,
|
||||||
@@ -57,8 +55,9 @@ export default function GamepadInputManager() {
|
|||||||
runMacro,
|
runMacro,
|
||||||
toggleNightVision,
|
toggleNightVision,
|
||||||
registerInputState,
|
registerInputState,
|
||||||
},
|
} = useControlActions();
|
||||||
} = useControlSystem();
|
const cameraAngle = useControlSelector((control) => control.state.camera?.angle);
|
||||||
|
const cameraConfig = useControlSelector((control) => control.state.camera?.config);
|
||||||
const dockAssist = useManualDockAssist();
|
const dockAssist = useManualDockAssist();
|
||||||
const { value: gamepadSettings, save: saveGamepadSettings } = useSettingsNamespace(
|
const { value: gamepadSettings, save: saveGamepadSettings } = useSettingsNamespace(
|
||||||
'gamepad',
|
'gamepad',
|
||||||
@@ -162,8 +161,8 @@ export default function GamepadInputManager() {
|
|||||||
// with the newest settings and control actions without resubscribing to the hub.
|
// with the newest settings and control actions without resubscribing to the hub.
|
||||||
latestRef.current = {
|
latestRef.current = {
|
||||||
activeSignature,
|
activeSignature,
|
||||||
cameraAngle: state.camera?.angle,
|
cameraAngle,
|
||||||
cameraConfig: state.camera?.config,
|
cameraConfig,
|
||||||
dockAssist,
|
dockAssist,
|
||||||
gamepadSettings,
|
gamepadSettings,
|
||||||
registerInputState,
|
registerInputState,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Keyboard Input Manager
|
// Keyboard Input Manager
|
||||||
// Purpose: Captures and translates keyboard events into normalized control intents. Scope: Owns keydown/keyup listeners and dispatch coordination for drive controls.
|
// Purpose: Captures and translates keyboard events into normalized control intents. Scope: Owns keydown/keyup listeners and dispatch coordination for drive controls.
|
||||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
||||||
import { useControlSystem } from '../ControlContext.jsx';
|
import { useControlActions, useControlSelector } from '../ControlContext.jsx';
|
||||||
import { useChat } from '../../context/ChatContext.jsx';
|
import { useChat } from '../../context/ChatContext.jsx';
|
||||||
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { normalizeKeymapEntries, tokensForEvent } from '../keymapUtils.js';
|
import { normalizeKeymapEntries, tokensForEvent } from '../keymapUtils.js';
|
||||||
@@ -83,8 +83,6 @@ function shouldIgnoreEvent(event) {
|
|||||||
|
|
||||||
export default function KeyboardInputManager() {
|
export default function KeyboardInputManager() {
|
||||||
const {
|
const {
|
||||||
state,
|
|
||||||
actions: {
|
|
||||||
setMode,
|
setMode,
|
||||||
setDriveVector,
|
setDriveVector,
|
||||||
setAuxMotors,
|
setAuxMotors,
|
||||||
@@ -98,15 +96,19 @@ export default function KeyboardInputManager() {
|
|||||||
setMicPttActive,
|
setMicPttActive,
|
||||||
setSongNote,
|
setSongNote,
|
||||||
sendSong,
|
sendSong,
|
||||||
},
|
} = useControlActions();
|
||||||
} = useControlSystem();
|
const rawKeymap = useControlSelector((control) => control.state.keymap);
|
||||||
|
const cameraNudgeDegrees = useControlSelector((control) => control.state.camera?.config?.nudgeDegrees);
|
||||||
|
const songNote = useControlSelector((control) => control.state.song?.note);
|
||||||
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
|
const hornActive = useControlSelector((control) => Boolean(control.state.horn?.active));
|
||||||
const homeAssistant = useSessionSelector((state) => state.session?.homeAssistant || null);
|
const homeAssistant = useSessionSelector((state) => state.session?.homeAssistant || null);
|
||||||
const dockAssist = useManualDockAssist();
|
const dockAssist = useManualDockAssist();
|
||||||
const { homeAssistantSetState } = useSessionActions();
|
const { homeAssistantSetState } = useSessionActions();
|
||||||
const { focusChat, isChatFocused } = useChat();
|
const { focusChat, isChatFocused } = useChat();
|
||||||
const { value: inputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
const { value: inputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
||||||
const { save: saveVideoSettings } = useSettingsNamespace('video', VIDEO_SETTINGS_DEFAULTS);
|
const { save: saveVideoSettings } = useSettingsNamespace('video', VIDEO_SETTINGS_DEFAULTS);
|
||||||
const keymap = useMemo(() => normalizeKeymapEntries(state.keymap), [state.keymap]);
|
const keymap = useMemo(() => normalizeKeymapEntries(rawKeymap), [rawKeymap]);
|
||||||
const actionTokens = useMemo(() => {
|
const actionTokens = useMemo(() => {
|
||||||
const tokens = new Set();
|
const tokens = new Set();
|
||||||
Object.values(keymap).forEach((bindingSet) => {
|
Object.values(keymap).forEach((bindingSet) => {
|
||||||
@@ -132,8 +134,8 @@ export default function KeyboardInputManager() {
|
|||||||
return mapTiltSpeedToInterval(tiltSpeed);
|
return mapTiltSpeedToInterval(tiltSpeed);
|
||||||
}, [inputSettings?.keyboard]);
|
}, [inputSettings?.keyboard]);
|
||||||
const servoStep = useMemo(
|
const servoStep = useMemo(
|
||||||
() => Math.abs(state.camera?.config?.nudgeDegrees || 1),
|
() => Math.abs(cameraNudgeDegrees || 1),
|
||||||
[state.camera?.config?.nudgeDegrees],
|
[cameraNudgeDegrees],
|
||||||
);
|
);
|
||||||
|
|
||||||
const activeTokensRef = useRef(new Set());
|
const activeTokensRef = useRef(new Set());
|
||||||
@@ -352,7 +354,7 @@ export default function KeyboardInputManager() {
|
|||||||
setMicPttActive,
|
setMicPttActive,
|
||||||
setMode,
|
setMode,
|
||||||
setSongNote,
|
setSongNote,
|
||||||
songNote: state.song?.note,
|
songNote,
|
||||||
startHorn,
|
startHorn,
|
||||||
stopAllMotion,
|
stopAllMotion,
|
||||||
stopHorn,
|
stopHorn,
|
||||||
@@ -455,11 +457,11 @@ export default function KeyboardInputManager() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
latestResetAllRef.current();
|
latestResetAllRef.current();
|
||||||
}, [state.roverId]);
|
}, [roverId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
hornActiveRef.current = Boolean(state.horn?.active);
|
hornActiveRef.current = hornActive;
|
||||||
}, [state.horn?.active]);
|
}, [hornActive]);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||||
|
|
||||||
export function useManualDockAssist(options = {}) {
|
export function useManualDockAssist(options = {}) {
|
||||||
const { manageLifecycle = false } = options;
|
const { manageLifecycle = false } = options;
|
||||||
const {
|
const roverId = useControlSelector((control) => control.state.roverId);
|
||||||
state: { roverId, manualDockAssist },
|
const active = useControlSelector((control) => Boolean(control.state.manualDockAssist?.active));
|
||||||
actions,
|
const actions = useControlActions();
|
||||||
} = useControlSystem();
|
|
||||||
const frame = useTelemetryFrame(roverId);
|
const frame = useTelemetryFrame(roverId);
|
||||||
const sensors = frame?.sensors || {};
|
const sensors = frame?.sensors || {};
|
||||||
const chargingLabel = sensors?.chargingState?.label || '';
|
const chargingLabel = sensors?.chargingState?.label || '';
|
||||||
const docked = Boolean(sensors?.chargingSources?.homeBase);
|
const docked = Boolean(sensors?.chargingSources?.homeBase);
|
||||||
const charging = docked && chargingLabel.toLowerCase() !== 'not charging' && chargingLabel !== '';
|
const charging = docked && chargingLabel.toLowerCase() !== 'not charging' && chargingLabel !== '';
|
||||||
const active = Boolean(manualDockAssist?.active);
|
|
||||||
const wasDockedRef = useRef(false);
|
const wasDockedRef = useRef(false);
|
||||||
|
|
||||||
const enterAssist = useCallback(() => {
|
const enterAssist = useCallback(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user