slopfixing / issue 001
@@ -0,0 +1,723 @@
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/",
|
||||
"runDir": "perf/results/2026-06-12T05-27-45-384Z-root-runtime",
|
||||
"environment": {
|
||||
"cpuThrottle": 6,
|
||||
"viewport": {
|
||||
"width": 390,
|
||||
"height": 844
|
||||
},
|
||||
"isMobile": true,
|
||||
"sampleMs": 30000
|
||||
},
|
||||
"metrics": {
|
||||
"Timestamp": 20904.330789,
|
||||
"AudioHandlers": 0,
|
||||
"AudioWorkletProcessors": 0,
|
||||
"Documents": 1,
|
||||
"Frames": 1,
|
||||
"JSEventListeners": 365,
|
||||
"LayoutObjects": 1886,
|
||||
"MediaKeySessions": 0,
|
||||
"MediaKeys": 0,
|
||||
"Nodes": 2224,
|
||||
"Resources": 24,
|
||||
"ContextLifecycleStateObservers": 14,
|
||||
"V8PerContextDatas": 2,
|
||||
"WorkerGlobalScopes": 0,
|
||||
"UACSSResources": 0,
|
||||
"RTCPeerConnections": 0,
|
||||
"ResourceFetchers": 1,
|
||||
"AdSubframes": 0,
|
||||
"DetachedScriptStates": 2,
|
||||
"ArrayBufferContents": 1,
|
||||
"LayoutCount": 11,
|
||||
"RecalcStyleCount": 12,
|
||||
"LayoutDuration": 0.81696,
|
||||
"RecalcStyleDuration": 0.7972,
|
||||
"DevToolsCommandDuration": 0.000356,
|
||||
"ScriptDuration": 26.612771,
|
||||
"V8CompileDuration": 0,
|
||||
"TaskDuration": 34.234417,
|
||||
"TaskOtherDuration": 6.00713,
|
||||
"ThreadTime": 35.26645,
|
||||
"ProcessTime": 42.01,
|
||||
"JSHeapUsedSize": 20964432,
|
||||
"JSHeapTotalSize": 35799040,
|
||||
"FirstMeaningfulPaint": 0,
|
||||
"DomContentLoaded": 20874.278609,
|
||||
"NavigationStart": 20868.27481
|
||||
},
|
||||
"snapshot": {
|
||||
"elapsedMs": 37773.099999997765,
|
||||
"listeners": [
|
||||
{
|
||||
"key": "add:input:invalid",
|
||||
"count": 18
|
||||
},
|
||||
{
|
||||
"key": "add:window:online",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"key": "add:document:visibilitychange",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"key": "remove:window:online",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"key": "remove:document:visibilitychange",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"key": "add:WebSocket:message",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"key": "add:document:fullscreenchange",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"key": "add:document:webkitfullscreenchange",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
],
|
||||
"timers": [
|
||||
{
|
||||
"key": "timeout:0",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"key": "interval:250",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"key": "interval:200",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"key": "timeout:15000",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "interval:1000",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "interval:2000",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "timeout:12000",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "timeout:4000",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "interval:60000",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "interval:30000",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"key": "timeout:10000",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"key": "timeout:592",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"timeouts": 14,
|
||||
"intervals": 15,
|
||||
"timeoutFires": 9,
|
||||
"intervalFires": 46,
|
||||
"webSocketMessageCount": 103,
|
||||
"webSocketBytes": 512324,
|
||||
"webSocketEvents": [
|
||||
{
|
||||
"event": "log:entry",
|
||||
"count": 62,
|
||||
"bytes": 14692
|
||||
},
|
||||
{
|
||||
"event": "session:sync",
|
||||
"count": 14,
|
||||
"bytes": 237697
|
||||
},
|
||||
{
|
||||
"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": 10972
|
||||
},
|
||||
{
|
||||
"event": "chat:init",
|
||||
"count": 2,
|
||||
"bytes": 155226
|
||||
},
|
||||
{
|
||||
"event": "overseer:memory",
|
||||
"count": 2,
|
||||
"bytes": 1040
|
||||
},
|
||||
{
|
||||
"event": "log:init",
|
||||
"count": 2,
|
||||
"bytes": 87839
|
||||
},
|
||||
{
|
||||
"event": "barcode:state",
|
||||
"count": 2,
|
||||
"bytes": 3254
|
||||
},
|
||||
{
|
||||
"event": "kinect:pointCloudFrame",
|
||||
"count": 2,
|
||||
"bytes": 524
|
||||
},
|
||||
{
|
||||
"event": "{\"type\":\"con",
|
||||
"count": 1,
|
||||
"bytes": 20
|
||||
},
|
||||
{
|
||||
"event": "0{\"sid\":\"SuW",
|
||||
"count": 1,
|
||||
"bytes": 106
|
||||
},
|
||||
{
|
||||
"event": "40{\"sid\":\"Wx",
|
||||
"count": 1,
|
||||
"bytes": 32
|
||||
},
|
||||
{
|
||||
"event": "0{\"sid\":\"mxD",
|
||||
"count": 1,
|
||||
"bytes": 106
|
||||
},
|
||||
{
|
||||
"event": "40{\"sid\":\"mW",
|
||||
"count": 1,
|
||||
"bytes": 32
|
||||
}
|
||||
],
|
||||
"mutationsTotal": {
|
||||
"records": 536,
|
||||
"added": 170,
|
||||
"removed": 11,
|
||||
"attrs": 346,
|
||||
"text": 9
|
||||
},
|
||||
"mutationTargets": [
|
||||
{
|
||||
"key": "input.accent-emerald-400:attributes:name",
|
||||
"count": 102
|
||||
},
|
||||
{
|
||||
"key": "input.accent-emerald-400:attributes:type",
|
||||
"count": 51
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.w-full:attributes:name",
|
||||
"count": 28
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.flex-1:attributes:name",
|
||||
"count": 24
|
||||
},
|
||||
{
|
||||
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:name",
|
||||
"count": 18
|
||||
},
|
||||
{
|
||||
"key": "input.accent-cyan-500:attributes:name",
|
||||
"count": 18
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.chat-composer-input:attributes:name",
|
||||
"count": 14
|
||||
},
|
||||
{
|
||||
"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": "Text:characterData:",
|
||||
"count": 9
|
||||
},
|
||||
{
|
||||
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:type",
|
||||
"count": 9
|
||||
},
|
||||
{
|
||||
"key": "header.flex.items-center:attributes:style",
|
||||
"count": 9
|
||||
},
|
||||
{
|
||||
"key": "input.accent-cyan-500:attributes:type",
|
||||
"count": 9
|
||||
},
|
||||
{
|
||||
"key": "input:attributes:type",
|
||||
"count": 7
|
||||
},
|
||||
{
|
||||
"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": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:value",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "div.fixed.bottom-2:attributes:class",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "input.min-w-0.flex-1:attributes:name",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"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": "span.inline-flex.items-center:attributes:class",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"key": "button.button-dark.min-w-0:attributes:disabled",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"mutationBursts": [
|
||||
{
|
||||
"at": 17875,
|
||||
"records": 171,
|
||||
"added": 117,
|
||||
"removed": 6,
|
||||
"attrs": 46,
|
||||
"text": 2
|
||||
},
|
||||
{
|
||||
"at": 24975,
|
||||
"records": 62,
|
||||
"added": 5,
|
||||
"removed": 0,
|
||||
"attrs": 57,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 37525,
|
||||
"records": 56,
|
||||
"added": 4,
|
||||
"removed": 2,
|
||||
"attrs": 50,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 29221,
|
||||
"records": 54,
|
||||
"added": 1,
|
||||
"removed": 1,
|
||||
"attrs": 50,
|
||||
"text": 2
|
||||
},
|
||||
{
|
||||
"at": 33394,
|
||||
"records": 51,
|
||||
"added": 0,
|
||||
"removed": 1,
|
||||
"attrs": 49,
|
||||
"text": 1
|
||||
},
|
||||
{
|
||||
"at": 29801,
|
||||
"records": 28,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 27,
|
||||
"text": 1
|
||||
},
|
||||
{
|
||||
"at": 30093,
|
||||
"records": 28,
|
||||
"added": 0,
|
||||
"removed": 1,
|
||||
"attrs": 27,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 245,
|
||||
"records": 27,
|
||||
"added": 27,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 11721,
|
||||
"records": 23,
|
||||
"added": 2,
|
||||
"removed": 0,
|
||||
"attrs": 20,
|
||||
"text": 1
|
||||
},
|
||||
{
|
||||
"at": 10636,
|
||||
"records": 20,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 20,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 218,
|
||||
"records": 6,
|
||||
"added": 6,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 272,
|
||||
"records": 3,
|
||||
"added": 3,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 227,
|
||||
"records": 2,
|
||||
"added": 2,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 352,
|
||||
"records": 2,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 2
|
||||
},
|
||||
{
|
||||
"at": 282,
|
||||
"records": 1,
|
||||
"added": 1,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 5981,
|
||||
"records": 1,
|
||||
"added": 1,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 8409,
|
||||
"records": 1,
|
||||
"added": 1,
|
||||
"removed": 0,
|
||||
"attrs": 0,
|
||||
"text": 0
|
||||
}
|
||||
],
|
||||
"frames": {
|
||||
"count": 65,
|
||||
"average": 571.7707692307692,
|
||||
"p95": 3433.0999999999985,
|
||||
"p99": 6416.4
|
||||
},
|
||||
"longTasks": {
|
||||
"count": 34,
|
||||
"top": [
|
||||
{
|
||||
"startTime": 12843.900000002235,
|
||||
"duration": 5032,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 25604.5,
|
||||
"duration": 3618,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 21451,
|
||||
"duration": 3524,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 34259.10000000149,
|
||||
"duration": 3267,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 30382.300000000745,
|
||||
"duration": 3012,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 3396.2000000029802,
|
||||
"duration": 2633,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 6155.400000002235,
|
||||
"duration": 2255,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 19590.10000000149,
|
||||
"duration": 1285,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 17883.60000000149,
|
||||
"duration": 1281,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 8914.300000000745,
|
||||
"duration": 653,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 9985.5,
|
||||
"duration": 651,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 11118.60000000149,
|
||||
"duration": 603,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 8423.300000000745,
|
||||
"duration": 370,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 24981.70000000298,
|
||||
"duration": 323,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 29579.20000000298,
|
||||
"duration": 222,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 30099.400000002235,
|
||||
"duration": 200,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 29914.10000000149,
|
||||
"duration": 179,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 25344,
|
||||
"duration": 177,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 21260.20000000298,
|
||||
"duration": 140,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 29228.300000000745,
|
||||
"duration": 124,
|
||||
"name": "self"
|
||||
}
|
||||
]
|
||||
},
|
||||
"consoleErrors": []
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 664 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 160 KiB |
@@ -0,0 +1,486 @@
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/",
|
||||
"runDir": "perf/results/2026-06-12T05-30-10-213Z",
|
||||
"elapsedMs": 184508,
|
||||
"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:5173/",
|
||||
"environment": {
|
||||
"cpuThrottle": 8,
|
||||
"viewport": {
|
||||
"width": 390,
|
||||
"height": 844
|
||||
},
|
||||
"isMobile": true
|
||||
},
|
||||
"metrics": {
|
||||
"afterLoad": {
|
||||
"Timestamp": 21026.536855,
|
||||
"Documents": 3,
|
||||
"Frames": 1,
|
||||
"JSEventListeners": 213,
|
||||
"Nodes": 310,
|
||||
"LayoutCount": 4,
|
||||
"RecalcStyleCount": 6,
|
||||
"LayoutDuration": 0.351349,
|
||||
"RecalcStyleDuration": 0.164,
|
||||
"ScriptDuration": 8.025346,
|
||||
"TaskDuration": 12.3993,
|
||||
"JSHeapUsedSize": 8055732,
|
||||
"JSHeapTotalSize": 9846784
|
||||
},
|
||||
"end": {
|
||||
"Timestamp": 21172.00552,
|
||||
"Documents": 1,
|
||||
"Frames": 1,
|
||||
"JSEventListeners": 776,
|
||||
"Nodes": 2366,
|
||||
"LayoutCount": 28,
|
||||
"RecalcStyleCount": 51,
|
||||
"LayoutDuration": 2.921363,
|
||||
"RecalcStyleDuration": 2.465639,
|
||||
"ScriptDuration": 125.009213,
|
||||
"TaskDuration": 154.711298,
|
||||
"JSHeapUsedSize": 49187212,
|
||||
"JSHeapTotalSize": 81772544
|
||||
},
|
||||
"delta": {
|
||||
"Timestamp": 145.46866500000033,
|
||||
"Documents": -2,
|
||||
"Frames": 0,
|
||||
"JSEventListeners": 563,
|
||||
"Nodes": 2056,
|
||||
"LayoutCount": 24,
|
||||
"RecalcStyleCount": 45,
|
||||
"LayoutDuration": 2.570014,
|
||||
"RecalcStyleDuration": 2.3016389999999998,
|
||||
"ScriptDuration": 116.983867,
|
||||
"TaskDuration": 142.311998,
|
||||
"JSHeapUsedSize": 41131480,
|
||||
"JSHeapTotalSize": 71925760
|
||||
}
|
||||
},
|
||||
"cpuSummary": {
|
||||
"totalProfileMs": 109222.492,
|
||||
"sampleCount": 82521,
|
||||
"topSelfTime": [
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=7eee8a21",
|
||||
"functionName": "exports.jsxDEV",
|
||||
"lineNumber": 238,
|
||||
"columnNumber": 31,
|
||||
"selfMs": 50684.10400000017,
|
||||
"samples": 40987
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "(program)",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 11988.050000000003,
|
||||
"samples": 3951
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/chunk-YX2XZWS3.js?v=7eee8a21",
|
||||
"functionName": "exports.createElement",
|
||||
"lineNumber": 760,
|
||||
"columnNumber": 38,
|
||||
"selfMs": 6174.760000000029,
|
||||
"samples": 5020
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "createTask",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 4341.4680000000035,
|
||||
"samples": 3581
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=7eee8a21",
|
||||
"functionName": "ReactElement",
|
||||
"lineNumber": 126,
|
||||
"columnNumber": 27,
|
||||
"selfMs": 4147.347000000001,
|
||||
"samples": 3387
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "run",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 2896.0120000000024,
|
||||
"samples": 2303
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "(idle)",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 1528.1939999999993,
|
||||
"samples": 1205
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "(garbage collector)",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 1454.5849999999996,
|
||||
"samples": 1076
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=7eee8a21",
|
||||
"functionName": "getTaskName",
|
||||
"lineNumber": 80,
|
||||
"columnNumber": 26,
|
||||
"selfMs": 1229.8500000000001,
|
||||
"samples": 1028
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "runWithFiberInDEV",
|
||||
"lineNumber": 992,
|
||||
"columnNumber": 32,
|
||||
"selfMs": 982.0620000000005,
|
||||
"samples": 808
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "now",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 786.4740000000004,
|
||||
"samples": 634
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "validateProperty",
|
||||
"lineNumber": 2344,
|
||||
"columnNumber": 31,
|
||||
"selfMs": 754.6519999999999,
|
||||
"samples": 611
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "updatedAncestorInfoDev",
|
||||
"lineNumber": 1990,
|
||||
"columnNumber": 37,
|
||||
"selfMs": 746.4530000000003,
|
||||
"samples": 607
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=7eee8a21",
|
||||
"functionName": "jsxDEVImpl",
|
||||
"lineNumber": 167,
|
||||
"columnNumber": 25,
|
||||
"selfMs": 731.4129999999996,
|
||||
"samples": 598
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "react_stack_bottom_frame",
|
||||
"lineNumber": 18504,
|
||||
"columnNumber": 42,
|
||||
"selfMs": 652.937,
|
||||
"samples": 533
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/chunk-YX2XZWS3.js?v=7eee8a21",
|
||||
"functionName": "ReactElement",
|
||||
"lineNumber": 165,
|
||||
"columnNumber": 27,
|
||||
"selfMs": 580.7530000000002,
|
||||
"samples": 462
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "updateProperties",
|
||||
"lineNumber": 14785,
|
||||
"columnNumber": 31,
|
||||
"selfMs": 517.7620000000002,
|
||||
"samples": 424
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "createWorkInProgress",
|
||||
"lineNumber": 3630,
|
||||
"columnNumber": 35,
|
||||
"selfMs": 508.02600000000007,
|
||||
"samples": 409
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "ReactStrictModeWarnings.recordLegacyContextWarning",
|
||||
"lineNumber": 18468,
|
||||
"columnNumber": 67,
|
||||
"selfMs": 495.80399999999986,
|
||||
"samples": 409
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "commitUpdate",
|
||||
"lineNumber": 15886,
|
||||
"columnNumber": 27,
|
||||
"selfMs": 477.684,
|
||||
"samples": 370
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "beginWork",
|
||||
"lineNumber": 8415,
|
||||
"columnNumber": 24,
|
||||
"selfMs": 446.36999999999983,
|
||||
"samples": 356
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "reconcileChildrenArray",
|
||||
"lineNumber": 4935,
|
||||
"columnNumber": 39,
|
||||
"selfMs": 436.57999999999987,
|
||||
"samples": 350
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/src/components/ChatMessageRow/index.jsx",
|
||||
"functionName": "ChatMessageRow",
|
||||
"lineNumber": 195,
|
||||
"columnNumber": 38,
|
||||
"selfMs": 413.4399999999997,
|
||||
"samples": 344
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=7eee8a21",
|
||||
"functionName": "getComponentNameFromType",
|
||||
"lineNumber": 12,
|
||||
"columnNumber": 39,
|
||||
"selfMs": 408.42100000000005,
|
||||
"samples": 327
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "addObjectDiffToProperties",
|
||||
"lineNumber": 3080,
|
||||
"columnNumber": 40,
|
||||
"selfMs": 388.74499999999983,
|
||||
"samples": 298
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "setValueForStyles",
|
||||
"lineNumber": 2231,
|
||||
"columnNumber": 32,
|
||||
"selfMs": 388.087,
|
||||
"samples": 311
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "measure",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 382.2370000000001,
|
||||
"samples": 317
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "(anonymous)",
|
||||
"lineNumber": 5231,
|
||||
"columnNumber": 23,
|
||||
"selfMs": 372.06799999999976,
|
||||
"samples": 277
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"functionName": "logComponentRender",
|
||||
"lineNumber": 3166,
|
||||
"columnNumber": 33,
|
||||
"selfMs": 342.3039999999999,
|
||||
"samples": 277
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:5173/src/context/SessionContext.jsx",
|
||||
"functionName": "(anonymous)",
|
||||
"lineNumber": 331,
|
||||
"columnNumber": 20,
|
||||
"selfMs": 337.47,
|
||||
"samples": 275
|
||||
}
|
||||
]
|
||||
},
|
||||
"responsiveness": {
|
||||
"longTaskCount": 83,
|
||||
"topLongTasks": [
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 81802.5,
|
||||
"duration": 17746
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 54463,
|
||||
"duration": 6694
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 99677.39999999851,
|
||||
"duration": 6024
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 21612.599999997765,
|
||||
"duration": 5858
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 110758.5,
|
||||
"duration": 5803
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 62108,
|
||||
"duration": 5772
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 76151.89999999851,
|
||||
"duration": 5650
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 125421.19999999925,
|
||||
"duration": 5648
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 145249.3999999985,
|
||||
"duration": 5587
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 132005.09999999776,
|
||||
"duration": 5514
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 68337.5,
|
||||
"duration": 5496
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 118182.59999999776,
|
||||
"duration": 5266
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 40730,
|
||||
"duration": 5224
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 138897,
|
||||
"duration": 5148
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 151428.19999999925,
|
||||
"duration": 5134
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 106092.80000000075,
|
||||
"duration": 4665
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 32223.39999999851,
|
||||
"duration": 4328
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 4601.89999999851,
|
||||
"duration": 3794
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 8585.39999999851,
|
||||
"duration": 3021
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 16030.39999999851,
|
||||
"duration": 2773
|
||||
}
|
||||
],
|
||||
"frameCount": 152,
|
||||
"averageFrameMs": 1041.5151315789474,
|
||||
"p95FrameMs": 5816.499999999985,
|
||||
"p99FrameMs": 10849.5,
|
||||
"intervalDriftCount": 37,
|
||||
"averageIntervalDriftMs": 3979.5216216216418,
|
||||
"p95IntervalDriftMs": 13818.400000002235,
|
||||
"p99IntervalDriftMs": 29643.89999999851
|
||||
},
|
||||
"consoleMessages": [
|
||||
{
|
||||
"type": "debug",
|
||||
"text": "[vite] connecting...",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:5173/@vite/client",
|
||||
"line": 732,
|
||||
"column": 8,
|
||||
"lineNumber": 732,
|
||||
"columnNumber": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "debug",
|
||||
"text": "[vite] connected.",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:5173/@vite/client",
|
||||
"line": 826,
|
||||
"column": 11,
|
||||
"lineNumber": 826,
|
||||
"columnNumber": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "info",
|
||||
"text": "%cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=7eee8a21",
|
||||
"line": 20102,
|
||||
"column": 53,
|
||||
"lineNumber": 20102,
|
||||
"columnNumber": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "info",
|
||||
"text": "[socket] connecting to https://rover.otter.land",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:5173/src/lib/socket.js",
|
||||
"line": 7,
|
||||
"column": 8,
|
||||
"lineNumber": 7,
|
||||
"columnNumber": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
],
|
||||
"failedRequests": [],
|
||||
"errorResponses": [
|
||||
{
|
||||
"url": "https://media.discordapp.net/attachments/1424887548622405672/1512626709692682340/cat2.PNG?ex=6a24c6e6&is=6a237566&hm=baeec4c8e666888d89711065b1dbcb391c3415f9c28d1917bdd286776b60c867&=&format=webp&quality=lossless",
|
||||
"status": 404
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/",
|
||||
"runDir": "perf/results/2026-06-12T05-34-37-713Z-root-runtime",
|
||||
"environment": {
|
||||
"cpuThrottle": 6,
|
||||
"viewport": {
|
||||
"width": 390,
|
||||
"height": 844
|
||||
},
|
||||
"isMobile": true,
|
||||
"sampleMs": 30000
|
||||
},
|
||||
"metrics": {
|
||||
"Timestamp": 21313.290308,
|
||||
"AudioHandlers": 0,
|
||||
"AudioWorkletProcessors": 0,
|
||||
"Documents": 1,
|
||||
"Frames": 1,
|
||||
"JSEventListeners": 1405,
|
||||
"LayoutObjects": 243,
|
||||
"MediaKeySessions": 0,
|
||||
"MediaKeys": 0,
|
||||
"Nodes": 353,
|
||||
"Resources": 0,
|
||||
"ContextLifecycleStateObservers": 7,
|
||||
"V8PerContextDatas": 2,
|
||||
"WorkerGlobalScopes": 0,
|
||||
"UACSSResources": 0,
|
||||
"RTCPeerConnections": 0,
|
||||
"ResourceFetchers": 1,
|
||||
"AdSubframes": 0,
|
||||
"DetachedScriptStates": 2,
|
||||
"ArrayBufferContents": 1,
|
||||
"LayoutCount": 5,
|
||||
"RecalcStyleCount": 6,
|
||||
"LayoutDuration": 0.274398,
|
||||
"RecalcStyleDuration": 0.205702,
|
||||
"DevToolsCommandDuration": 0.000402,
|
||||
"ScriptDuration": 22.219943,
|
||||
"V8CompileDuration": 0,
|
||||
"TaskDuration": 31.430573,
|
||||
"TaskOtherDuration": 8.730128,
|
||||
"ThreadTime": 31.88879,
|
||||
"ProcessTime": 36.72,
|
||||
"JSHeapUsedSize": 5442836,
|
||||
"JSHeapTotalSize": 9699328,
|
||||
"FirstMeaningfulPaint": 0,
|
||||
"DomContentLoaded": 21283.254568,
|
||||
"NavigationStart": 21280.58028
|
||||
},
|
||||
"snapshot": {
|
||||
"elapsedMs": 32642,
|
||||
"listeners": [
|
||||
{
|
||||
"key": "add:input:invalid",
|
||||
"count": 9
|
||||
},
|
||||
{
|
||||
"key": "add:WebSocket:message",
|
||||
"count": 7
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:pointerout",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:pointerover",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:pointerup",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:reset",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:submit",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:touchcancel",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:touchend",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:touchstart",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:touchmove",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "add:div#root:wheel",
|
||||
"count": 2
|
||||
}
|
||||
],
|
||||
"timers": [
|
||||
{
|
||||
"key": "timeout:15000",
|
||||
"count": 7
|
||||
},
|
||||
{
|
||||
"key": "timeout:5000",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"key": "timeout:0",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"key": "interval:1000",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"key": "interval:2000",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"key": "timeout:1299",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"key": "timeout:1507",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"timeouts": 17,
|
||||
"intervals": 2,
|
||||
"timeoutFires": 9,
|
||||
"intervalFires": 42,
|
||||
"webSocketMessageCount": 0,
|
||||
"webSocketBytes": 0,
|
||||
"webSocketEvents": [],
|
||||
"mutationsTotal": {
|
||||
"records": 6000,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6000,
|
||||
"text": 0
|
||||
},
|
||||
"mutationTargets": [
|
||||
{
|
||||
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:name",
|
||||
"count": 8054
|
||||
},
|
||||
{
|
||||
"key": "input.accent-emerald-400:attributes:name",
|
||||
"count": 8054
|
||||
},
|
||||
{
|
||||
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:type",
|
||||
"count": 4027
|
||||
},
|
||||
{
|
||||
"key": "input.accent-emerald-400:attributes:type",
|
||||
"count": 4027
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.w-full:attributes:name",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.flex-1:attributes:name",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.chat-composer-input:attributes:name",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"key": "input:attributes:name",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"key": "input.field-input.w-full:attributes:type",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"key": "Text:characterData:",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "input:attributes:type",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"key": "input#replay-sources-mobile-portrait-title.field-input.min-w-0:attributes:value",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"mutationBursts": [
|
||||
{
|
||||
"at": 26663,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26667,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26670,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26676,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26679,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26682,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26684,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26689,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26696,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26700,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26704,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26711,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26715,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26719,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26726,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26730,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26732,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26737,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26744,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
},
|
||||
{
|
||||
"at": 26748,
|
||||
"records": 6,
|
||||
"added": 0,
|
||||
"removed": 0,
|
||||
"attrs": 6,
|
||||
"text": 0
|
||||
}
|
||||
],
|
||||
"frames": {
|
||||
"count": 1678,
|
||||
"average": 19.307985697258644,
|
||||
"p95": 16.700000000000728,
|
||||
"p99": 16.80000000000291
|
||||
},
|
||||
"longTasks": {
|
||||
"count": 8,
|
||||
"top": [
|
||||
{
|
||||
"startTime": 625.4000000022352,
|
||||
"duration": 2057,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 2705.400000002235,
|
||||
"duration": 1120,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 3825.900000002235,
|
||||
"duration": 400,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 4491.699999999255,
|
||||
"duration": 196,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 4237.300000000745,
|
||||
"duration": 158,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 4931.5,
|
||||
"duration": 115,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 4828.699999999255,
|
||||
"duration": 101,
|
||||
"name": "self"
|
||||
},
|
||||
{
|
||||
"startTime": 25127.300000000745,
|
||||
"duration": 67,
|
||||
"name": "self"
|
||||
}
|
||||
]
|
||||
},
|
||||
"consoleErrors": [
|
||||
"connect_error undefined websocket error undefined",
|
||||
"connect_error undefined websocket error undefined",
|
||||
"connect_error undefined websocket error undefined",
|
||||
"connect_error undefined websocket error undefined",
|
||||
"connect_error undefined websocket error undefined",
|
||||
"connect_error undefined websocket error undefined",
|
||||
"connect_error undefined websocket error undefined"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 336 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,652 @@
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/",
|
||||
"runDir": "perf/results/2026-06-12T05-37-11-679Z",
|
||||
"elapsedMs": 48144,
|
||||
"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": 21442.422344,
|
||||
"Documents": 3,
|
||||
"Frames": 1,
|
||||
"JSEventListeners": 333,
|
||||
"Nodes": 364,
|
||||
"LayoutCount": 5,
|
||||
"RecalcStyleCount": 7,
|
||||
"LayoutDuration": 0.567739,
|
||||
"RecalcStyleDuration": 0.239019,
|
||||
"ScriptDuration": 5.57054,
|
||||
"TaskDuration": 7.681989,
|
||||
"JSHeapUsedSize": 3910380,
|
||||
"JSHeapTotalSize": 6815744
|
||||
},
|
||||
"end": {
|
||||
"Timestamp": 21481.571566,
|
||||
"Documents": 3,
|
||||
"Frames": 1,
|
||||
"JSEventListeners": 3020,
|
||||
"Nodes": 424,
|
||||
"LayoutCount": 10,
|
||||
"RecalcStyleCount": 20,
|
||||
"LayoutDuration": 0.71593,
|
||||
"RecalcStyleDuration": 0.592888,
|
||||
"ScriptDuration": 25.51692,
|
||||
"TaskDuration": 45.394233,
|
||||
"JSHeapUsedSize": 7257516,
|
||||
"JSHeapTotalSize": 8486912
|
||||
},
|
||||
"delta": {
|
||||
"Timestamp": 39.14922200000001,
|
||||
"Documents": 0,
|
||||
"Frames": 0,
|
||||
"JSEventListeners": 2687,
|
||||
"Nodes": 60,
|
||||
"LayoutCount": 5,
|
||||
"RecalcStyleCount": 13,
|
||||
"LayoutDuration": 0.14819099999999996,
|
||||
"RecalcStyleDuration": 0.353869,
|
||||
"ScriptDuration": 19.946379999999998,
|
||||
"TaskDuration": 37.712244,
|
||||
"JSHeapUsedSize": 3347136,
|
||||
"JSHeapTotalSize": 1671168
|
||||
}
|
||||
},
|
||||
"cpuSummary": {
|
||||
"totalProfileMs": 36856.206,
|
||||
"sampleCount": 27449,
|
||||
"topSelfTime": [
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "(program)",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 16008.739999999993,
|
||||
"samples": 10998
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "postMessage",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 1400.7229999999997,
|
||||
"samples": 1076
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "(idle)",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 1159.0759999999996,
|
||||
"samples": 921
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "lt",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 7349,
|
||||
"selfMs": 939.4450000000005,
|
||||
"samples": 747
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "ad",
|
||||
"lineNumber": 9,
|
||||
"columnNumber": 7898,
|
||||
"selfMs": 678.3770000000004,
|
||||
"samples": 534
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "Nc",
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 94591,
|
||||
"selfMs": 648.5520000000006,
|
||||
"samples": 533
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "(anonymous)",
|
||||
"lineNumber": 17,
|
||||
"columnNumber": 58982,
|
||||
"selfMs": 609.3419999999993,
|
||||
"samples": 479
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "requestAnimationFrame",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 569.4970000000001,
|
||||
"samples": 461
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "C9",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 94304,
|
||||
"selfMs": 480.7910000000001,
|
||||
"samples": 384
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "Z4",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 28258,
|
||||
"selfMs": 403.8930000000001,
|
||||
"samples": 305
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "qa",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 23434,
|
||||
"selfMs": 401.37100000000004,
|
||||
"samples": 328
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "queueMicrotask",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 378.9960000000001,
|
||||
"samples": 289
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "B6",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 34145,
|
||||
"selfMs": 375.95699999999994,
|
||||
"samples": 307
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "a2",
|
||||
"lineNumber": 9,
|
||||
"columnNumber": 71,
|
||||
"selfMs": 349.62499999999983,
|
||||
"samples": 295
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "c5",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 34654,
|
||||
"selfMs": 345.6060000000002,
|
||||
"samples": 274
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "K4",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 79484,
|
||||
"selfMs": 314.27200000000005,
|
||||
"samples": 240
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "we",
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 73874,
|
||||
"selfMs": 306.5540000000002,
|
||||
"samples": 251
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "C1",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 40723,
|
||||
"selfMs": 284.553,
|
||||
"samples": 221
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "Eh",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 95724,
|
||||
"selfMs": 280.467,
|
||||
"samples": 229
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "Kl",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 117211,
|
||||
"selfMs": 277.51900000000006,
|
||||
"samples": 215
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "U8",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 7096,
|
||||
"selfMs": 276.48700000000014,
|
||||
"samples": 222
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "z2",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 49302,
|
||||
"selfMs": 276.39199999999994,
|
||||
"samples": 206
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "Kt",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 48095,
|
||||
"selfMs": 273.0230000000001,
|
||||
"samples": 220
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "(garbage collector)",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 268.44300000000004,
|
||||
"samples": 207
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "d1",
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 70367,
|
||||
"selfMs": 268.08499999999987,
|
||||
"samples": 219
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "zl",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 80827,
|
||||
"selfMs": 263.652,
|
||||
"samples": 209
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "now",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 259.23800000000006,
|
||||
"samples": 210
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "Fl",
|
||||
"lineNumber": 8,
|
||||
"columnNumber": 101199,
|
||||
"selfMs": 229.16799999999992,
|
||||
"samples": 178
|
||||
},
|
||||
{
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"functionName": "$",
|
||||
"lineNumber": 1,
|
||||
"columnNumber": 10516,
|
||||
"selfMs": 228.36400000000006,
|
||||
"samples": 178
|
||||
},
|
||||
{
|
||||
"url": "",
|
||||
"functionName": "removeAttribute",
|
||||
"lineNumber": -1,
|
||||
"columnNumber": -1,
|
||||
"selfMs": 224.73000000000005,
|
||||
"samples": 182
|
||||
}
|
||||
]
|
||||
},
|
||||
"responsiveness": {
|
||||
"longTaskCount": 19,
|
||||
"topLongTasks": [
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 577.8000000007451,
|
||||
"duration": 2326
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 2934,
|
||||
"duration": 1390
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 4326.39999999851,
|
||||
"duration": 671
|
||||
},
|
||||
{
|
||||
"name": "unknown",
|
||||
"startTime": 8756.5,
|
||||
"duration": 354
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 5309,
|
||||
"duration": 284
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 5012.89999999851,
|
||||
"duration": 204
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 5968.5,
|
||||
"duration": 199
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 28664.800000000745,
|
||||
"duration": 144
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 5824.800000000745,
|
||||
"duration": 142
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 29231.199999999255,
|
||||
"duration": 118
|
||||
},
|
||||
{
|
||||
"name": "unknown",
|
||||
"startTime": 9170.599999997765,
|
||||
"duration": 107
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 29349.599999997765,
|
||||
"duration": 95
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 28837.89999999851,
|
||||
"duration": 89
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 27044.800000000745,
|
||||
"duration": 80
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 9278.300000000745,
|
||||
"duration": 73
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 30803.39999999851,
|
||||
"duration": 70
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 5632.39999999851,
|
||||
"duration": 60
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 9110.800000000745,
|
||||
"duration": 59
|
||||
},
|
||||
{
|
||||
"name": "self",
|
||||
"startTime": 9434.800000000745,
|
||||
"duration": 53
|
||||
}
|
||||
],
|
||||
"frameCount": 2103,
|
||||
"averageFrameMs": 22.205515929624347,
|
||||
"p95FrameMs": 16.799999999999272,
|
||||
"p99FrameMs": 100,
|
||||
"intervalDriftCount": 155,
|
||||
"averageIntervalDriftMs": 52.86322580645642,
|
||||
"p95IntervalDriftMs": 113.59999999776483,
|
||||
"p99IntervalDriftMs": 2354.099999997765
|
||||
},
|
||||
"consoleMessages": [
|
||||
{
|
||||
"type": "info",
|
||||
"text": "[socket] connecting to http://127.0.0.1:4173",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63768,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63768
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "WebSocket connection to 'ws://127.0.0.1:4173/socket.io/?EIO=4&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 0,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "error",
|
||||
"text": "connect_error undefined websocket error undefined",
|
||||
"location": {
|
||||
"url": "http://127.0.0.1:4173/assets/index-DhJId5qi.js",
|
||||
"line": 11,
|
||||
"column": 63988,
|
||||
"lineNumber": 11,
|
||||
"columnNumber": 63988
|
||||
}
|
||||
}
|
||||
],
|
||||
"failedRequests": [],
|
||||
"errorResponses": []
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-l3JqdlyF.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DhJId5qi.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BVKa8Zph.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Gamepad Input Manager
|
||||
// Purpose: Converts polled gamepad state into normalized control actions/commands. Scope: Integrates bindings, deadzone math, and dispatch callbacks for driving.
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
||||
import { useControlSystem } from '../ControlContext.jsx';
|
||||
import { useSettingsNamespace } from '../../settings/index.js';
|
||||
import { GAMEPAD_SETTINGS_DEFAULTS, GAMEPAD_PROFILE_DEFAULT } from '../../settings/namespaces.js';
|
||||
@@ -73,30 +73,33 @@ export default function GamepadInputManager() {
|
||||
const lastAuxSentAtRef = useRef(0);
|
||||
const lastServoAtRef = useRef(0);
|
||||
const lastServoAngleRef = useRef(null);
|
||||
// The hub subscription is intentionally stable, so this ref is the bridge back to the latest
|
||||
// React values. Rewriting it after each commit is cheaper than tearing down browser gamepad
|
||||
// listeners every time settings, camera state, or control callbacks change.
|
||||
const latestRef = useRef(null);
|
||||
|
||||
const ensureProfile = useCallback(
|
||||
(padState) => {
|
||||
const signature = padState.signature ?? getPadSignature(padState);
|
||||
if (gamepadSettings?.profiles?.[signature] || profileCacheRef.current.has(signature)) {
|
||||
return;
|
||||
}
|
||||
profileCacheRef.current.add(signature);
|
||||
saveGamepadSettings((prev) => {
|
||||
const current = prev ?? GAMEPAD_SETTINGS_DEFAULTS;
|
||||
if (current.profiles?.[signature]) return current;
|
||||
const base = current?.defaults?.profile ?? GAMEPAD_PROFILE_DEFAULT;
|
||||
const nextProfile = createProfileForPad(padState, base);
|
||||
return {
|
||||
...current,
|
||||
profiles: {
|
||||
...(current.profiles ?? {}),
|
||||
[signature]: nextProfile,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
[gamepadSettings?.profiles, saveGamepadSettings],
|
||||
);
|
||||
const ensureProfile = useCallback((padState) => {
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return;
|
||||
const signature = padState.signature ?? getPadSignature(padState);
|
||||
if (latest.gamepadSettings?.profiles?.[signature] || profileCacheRef.current.has(signature)) {
|
||||
return;
|
||||
}
|
||||
profileCacheRef.current.add(signature);
|
||||
latest.saveGamepadSettings((prev) => {
|
||||
const current = prev ?? GAMEPAD_SETTINGS_DEFAULTS;
|
||||
if (current.profiles?.[signature]) return current;
|
||||
const base = current?.defaults?.profile ?? GAMEPAD_PROFILE_DEFAULT;
|
||||
const nextProfile = createProfileForPad(padState, base);
|
||||
return {
|
||||
...current,
|
||||
profiles: {
|
||||
...(current.profiles ?? {}),
|
||||
[signature]: nextProfile,
|
||||
},
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleButtonEdge = useCallback((key, pressed) => {
|
||||
const prev = buttonStateRef.current.get(key) || false;
|
||||
@@ -104,97 +107,118 @@ export default function GamepadInputManager() {
|
||||
return pressed && !prev;
|
||||
}, []);
|
||||
|
||||
const handleCameraAxis = useCallback(
|
||||
(axisValue, calibration) => {
|
||||
const config = state.camera?.config;
|
||||
if (!config) return;
|
||||
const now = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
||||
const cameraMode = calibration?.cameraMode ?? 'absolute';
|
||||
const sensitivity = Math.max(1, Math.min(180, calibration?.cameraSensitivity ?? 60));
|
||||
if (cameraMode === 'velocity') {
|
||||
const dt = Math.min(50, now - lastServoAtRef.current || 16);
|
||||
const delta = axisValue * sensitivity * (dt / 1000);
|
||||
if (Math.abs(delta) < 0.01) return;
|
||||
const baseline =
|
||||
typeof lastServoAngleRef.current === 'number'
|
||||
? lastServoAngleRef.current
|
||||
: typeof state.camera?.angle === 'number'
|
||||
? state.camera.angle
|
||||
: typeof config.homeAngle === 'number'
|
||||
? config.homeAngle
|
||||
: 0;
|
||||
const nextAngle = baseline + delta;
|
||||
setServoAngle(nextAngle);
|
||||
lastServoAngleRef.current = nextAngle;
|
||||
lastServoAtRef.current = now;
|
||||
return;
|
||||
}
|
||||
const min = typeof config.minAngle === 'number' ? config.minAngle : -45;
|
||||
const max = typeof config.maxAngle === 'number' ? config.maxAngle : 45;
|
||||
const home = typeof config.homeAngle === 'number' ? config.homeAngle : (min + max) / 2;
|
||||
const angle =
|
||||
axisValue < 0
|
||||
? home + axisValue * (home - min)
|
||||
: home + axisValue * (max - home);
|
||||
if (
|
||||
typeof lastServoAngleRef.current === 'number' &&
|
||||
Math.abs(lastServoAngleRef.current - angle) < 0.35 &&
|
||||
now - lastServoAtRef.current < 80
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const handleCameraAxis = useCallback((axisValue, calibration) => {
|
||||
const latest = latestRef.current;
|
||||
const config = latest?.cameraConfig;
|
||||
if (!latest || !config) return;
|
||||
const now = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
||||
const cameraMode = calibration?.cameraMode ?? 'absolute';
|
||||
const sensitivity = Math.max(1, Math.min(180, calibration?.cameraSensitivity ?? 60));
|
||||
if (cameraMode === 'velocity') {
|
||||
const dt = Math.min(50, now - lastServoAtRef.current || 16);
|
||||
const delta = axisValue * sensitivity * (dt / 1000);
|
||||
if (Math.abs(delta) < 0.01) return;
|
||||
const baseline =
|
||||
typeof lastServoAngleRef.current === 'number'
|
||||
? lastServoAngleRef.current
|
||||
: typeof latest.cameraAngle === 'number'
|
||||
? latest.cameraAngle
|
||||
: typeof config.homeAngle === 'number'
|
||||
? config.homeAngle
|
||||
: 0;
|
||||
const nextAngle = baseline + delta;
|
||||
latest.setServoAngle(nextAngle);
|
||||
lastServoAngleRef.current = nextAngle;
|
||||
lastServoAtRef.current = now;
|
||||
lastServoAngleRef.current = angle;
|
||||
setServoAngle(angle);
|
||||
},
|
||||
[setServoAngle, state.camera?.angle, state.camera?.config],
|
||||
);
|
||||
return;
|
||||
}
|
||||
const min = typeof config.minAngle === 'number' ? config.minAngle : -45;
|
||||
const max = typeof config.maxAngle === 'number' ? config.maxAngle : 45;
|
||||
const home = typeof config.homeAngle === 'number' ? config.homeAngle : (min + max) / 2;
|
||||
const angle =
|
||||
axisValue < 0
|
||||
? home + axisValue * (home - min)
|
||||
: home + axisValue * (max - home);
|
||||
if (
|
||||
typeof lastServoAngleRef.current === 'number' &&
|
||||
Math.abs(lastServoAngleRef.current - angle) < 0.35 &&
|
||||
now - lastServoAtRef.current < 80
|
||||
) {
|
||||
return;
|
||||
}
|
||||
lastServoAtRef.current = now;
|
||||
lastServoAngleRef.current = angle;
|
||||
latest.setServoAngle(angle);
|
||||
}, []);
|
||||
|
||||
const activeSignature = useMemo(
|
||||
() => gamepadSettings?.activeSignature ?? null,
|
||||
[gamepadSettings?.activeSignature],
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
// Layout timing matters because the requestAnimationFrame gamepad poll can run immediately
|
||||
// after React commits. Updating this ref before paint keeps the stable hub callback aligned
|
||||
// with the newest settings and control actions without resubscribing to the hub.
|
||||
latestRef.current = {
|
||||
activeSignature,
|
||||
cameraAngle: state.camera?.angle,
|
||||
cameraConfig: state.camera?.config,
|
||||
dockAssist,
|
||||
gamepadSettings,
|
||||
registerInputState,
|
||||
runMacro,
|
||||
saveGamepadSettings,
|
||||
setAuxMotors,
|
||||
setDriveVector,
|
||||
setMode,
|
||||
setServoAngle,
|
||||
toggleNightVision,
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
return subscribeGamepadHub((hubState) => {
|
||||
const activePad = pickActivePad(hubState.pads, activeSignature);
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return;
|
||||
const activePad = pickActivePad(hubState.pads, latest.activeSignature);
|
||||
if (!activePad) {
|
||||
if (!areVectorsEqual(lastVectorRef.current, ZERO_VECTOR)) {
|
||||
lastVectorRef.current = ZERO_VECTOR;
|
||||
setDriveVector(ZERO_VECTOR, { source: SOURCE });
|
||||
latest.setDriveVector(ZERO_VECTOR, { source: SOURCE });
|
||||
}
|
||||
if (!areAuxEqual(lastAuxRef.current, ZERO_AUX)) {
|
||||
lastAuxRef.current = ZERO_AUX;
|
||||
setAuxMotors(ZERO_AUX);
|
||||
latest.setAuxMotors(ZERO_AUX);
|
||||
}
|
||||
buttonStateRef.current = new Map();
|
||||
reverseStateRef.current = { main: false, side: false };
|
||||
lastDriveSentAtRef.current = 0;
|
||||
lastAuxSentAtRef.current = 0;
|
||||
registerInputState(SOURCE, { connected: false });
|
||||
latest.registerInputState(SOURCE, { connected: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTextEntryActive()) {
|
||||
if (!areVectorsEqual(lastVectorRef.current, ZERO_VECTOR)) {
|
||||
lastVectorRef.current = ZERO_VECTOR;
|
||||
setDriveVector(ZERO_VECTOR, { source: SOURCE });
|
||||
latest.setDriveVector(ZERO_VECTOR, { source: SOURCE });
|
||||
}
|
||||
if (!areAuxEqual(lastAuxRef.current, ZERO_AUX)) {
|
||||
lastAuxRef.current = ZERO_AUX;
|
||||
setAuxMotors(ZERO_AUX);
|
||||
latest.setAuxMotors(ZERO_AUX);
|
||||
}
|
||||
buttonStateRef.current = new Map();
|
||||
reverseStateRef.current = { main: false, side: false };
|
||||
registerInputState(SOURCE, { connected: true, blocked: true });
|
||||
latest.registerInputState(SOURCE, { connected: true, blocked: true });
|
||||
return;
|
||||
}
|
||||
|
||||
ensureProfile(activePad);
|
||||
const signature = activePad.signature;
|
||||
const profile =
|
||||
gamepadSettings?.profiles?.[signature] ??
|
||||
gamepadSettings?.defaults?.profile ??
|
||||
latest.gamepadSettings?.profiles?.[signature] ??
|
||||
latest.gamepadSettings?.defaults?.profile ??
|
||||
GAMEPAD_PROFILE_DEFAULT;
|
||||
const outputs = computeGamepadOutputs(activePad, profile);
|
||||
|
||||
@@ -204,7 +228,7 @@ export default function GamepadInputManager() {
|
||||
if (idle || now - lastDriveSentAtRef.current >= DRIVE_RATE_MS) {
|
||||
lastVectorRef.current = outputs.driveVector;
|
||||
lastDriveSentAtRef.current = now;
|
||||
setDriveVector(outputs.driveVector, { source: SOURCE });
|
||||
latest.setDriveVector(outputs.driveVector, { source: SOURCE });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +252,7 @@ export default function GamepadInputManager() {
|
||||
if (isAuxIdle(aux) || now - lastAuxSentAtRef.current >= AUX_RATE_MS) {
|
||||
lastAuxRef.current = aux;
|
||||
lastAuxSentAtRef.current = now;
|
||||
setAuxMotors(aux);
|
||||
latest.setAuxMotors(aux);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,21 +269,21 @@ export default function GamepadInputManager() {
|
||||
}
|
||||
|
||||
if (outputs.buttons.driveMacro && handleButtonEdge('driveMacro', true)) {
|
||||
dockAssist.exitAssist();
|
||||
setMode('drive');
|
||||
runMacro('drive-sequence');
|
||||
latest.dockAssist.exitAssist();
|
||||
latest.setMode('drive');
|
||||
latest.runMacro('drive-sequence');
|
||||
} else if (!outputs.buttons.driveMacro) {
|
||||
handleButtonEdge('driveMacro', false);
|
||||
}
|
||||
|
||||
if (outputs.buttons.dockMacro && handleButtonEdge('dockMacro', true)) {
|
||||
dockAssist.toggleAssist();
|
||||
latest.dockAssist.toggleAssist();
|
||||
} else if (!outputs.buttons.dockMacro) {
|
||||
handleButtonEdge('dockMacro', false);
|
||||
}
|
||||
|
||||
if (outputs.buttons.nightVisionToggle && handleButtonEdge('nightVisionToggle', true)) {
|
||||
toggleNightVision();
|
||||
latest.toggleNightVision();
|
||||
} else if (!outputs.buttons.nightVisionToggle) {
|
||||
handleButtonEdge('nightVisionToggle', false);
|
||||
}
|
||||
@@ -268,7 +292,7 @@ export default function GamepadInputManager() {
|
||||
handleCameraAxis(outputs.cameraAxis, profile.calibration);
|
||||
}
|
||||
|
||||
registerInputState(SOURCE, {
|
||||
latest.registerInputState(SOURCE, {
|
||||
connected: true,
|
||||
signature,
|
||||
id: activePad.id,
|
||||
@@ -281,21 +305,7 @@ export default function GamepadInputManager() {
|
||||
bindings: outputs.sources,
|
||||
});
|
||||
});
|
||||
}, [
|
||||
activeSignature,
|
||||
ensureProfile,
|
||||
gamepadSettings?.defaults?.profile,
|
||||
gamepadSettings?.profiles,
|
||||
handleButtonEdge,
|
||||
handleCameraAxis,
|
||||
registerInputState,
|
||||
runMacro,
|
||||
setAuxMotors,
|
||||
setDriveVector,
|
||||
setMode,
|
||||
toggleNightVision,
|
||||
dockAssist,
|
||||
]);
|
||||
}, [ensureProfile, handleButtonEdge, handleCameraAxis]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Keyboard Input Manager
|
||||
// Purpose: Captures and translates keyboard events into normalized control intents. Scope: Owns keydown/keyup listeners and dispatch coordination for drive controls.
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
||||
import { useControlSystem } from '../ControlContext.jsx';
|
||||
import { useChat } from '../../context/ChatContext.jsx';
|
||||
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
@@ -103,7 +103,7 @@ export default function KeyboardInputManager() {
|
||||
const homeAssistant = useSessionSelector((state) => state.session?.homeAssistant || null);
|
||||
const dockAssist = useManualDockAssist();
|
||||
const { homeAssistantSetState } = useSessionActions();
|
||||
const { focusChat, blurChat, isChatFocused } = useChat();
|
||||
const { focusChat, isChatFocused } = useChat();
|
||||
const { value: inputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
|
||||
const { save: saveVideoSettings } = useSettingsNamespace('video', VIDEO_SETTINGS_DEFAULTS);
|
||||
const keymap = useMemo(() => normalizeKeymapEntries(state.keymap), [state.keymap]);
|
||||
@@ -142,19 +142,25 @@ export default function KeyboardInputManager() {
|
||||
const servoIntervalRef = useRef(null);
|
||||
const songIntervalRef = useRef(null);
|
||||
const hornActiveRef = useRef(false);
|
||||
// Global keyboard listeners must stay mounted while React state churns underneath them. This
|
||||
// ref is rewritten after every commit so the stable handlers can still use fresh settings,
|
||||
// keymaps, and action callbacks without making listener registration depend on those values.
|
||||
const latestRef = useRef(null);
|
||||
|
||||
const driveFromKeys = useCallback(() => {
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return;
|
||||
const tokensSnapshot = new Set(activeTokensRef.current);
|
||||
const speedOptions = getKeyboardDriveSpeedOptions(tokensSnapshot, keymap, keyboardSpeeds);
|
||||
const vector = computeKeyboardDriveVector(tokensSnapshot, keymap);
|
||||
const aux = computeKeyboardAuxMotors(tokensSnapshot, keymap);
|
||||
const speedOptions = getKeyboardDriveSpeedOptions(tokensSnapshot, latest.keymap, latest.keyboardSpeeds);
|
||||
const vector = computeKeyboardDriveVector(tokensSnapshot, latest.keymap);
|
||||
const aux = computeKeyboardAuxMotors(tokensSnapshot, latest.keymap);
|
||||
if (
|
||||
vector.x !== lastVectorRef.current.x ||
|
||||
vector.y !== lastVectorRef.current.y ||
|
||||
vector.boost !== lastVectorRef.current.boost
|
||||
) {
|
||||
lastVectorRef.current = vector;
|
||||
setDriveVector(vector, { source: SOURCE, speedOptions });
|
||||
latest.setDriveVector(vector, { source: SOURCE, speedOptions });
|
||||
}
|
||||
if (
|
||||
aux.main !== lastAuxRef.current.main ||
|
||||
@@ -162,14 +168,14 @@ export default function KeyboardInputManager() {
|
||||
aux.vacuum !== lastAuxRef.current.vacuum
|
||||
) {
|
||||
lastAuxRef.current = aux;
|
||||
setAuxMotors(aux);
|
||||
latest.setAuxMotors(aux);
|
||||
}
|
||||
registerInputState(SOURCE, {
|
||||
latest.registerInputState(SOURCE, {
|
||||
keys: Array.from(tokensSnapshot),
|
||||
vector,
|
||||
aux,
|
||||
});
|
||||
}, [keymap, keyboardSpeeds, registerInputState, setAuxMotors, setDriveVector]);
|
||||
}, []);
|
||||
|
||||
const stopServoLoop = useCallback(() => {
|
||||
if (servoIntervalRef.current) {
|
||||
@@ -179,11 +185,13 @@ export default function KeyboardInputManager() {
|
||||
}, []);
|
||||
|
||||
const computeServoDirection = useCallback(() => {
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return 0;
|
||||
const tokensSnapshot = new Set(activeTokensRef.current);
|
||||
const up = bindingActive(keymap.cameraUp, tokensSnapshot);
|
||||
const down = bindingActive(keymap.cameraDown, tokensSnapshot);
|
||||
const up = bindingActive(latest.keymap.cameraUp, tokensSnapshot);
|
||||
const down = bindingActive(latest.keymap.cameraDown, tokensSnapshot);
|
||||
return (up ? 1 : 0) - (down ? 1 : 0);
|
||||
}, [keymap]);
|
||||
}, []);
|
||||
|
||||
const ensureServoLoop = useCallback(() => {
|
||||
const direction = computeServoDirection();
|
||||
@@ -200,11 +208,16 @@ export default function KeyboardInputManager() {
|
||||
stopServoLoop();
|
||||
return;
|
||||
}
|
||||
nudgeServo(nextDirection * servoStep);
|
||||
servoIntervalRef.current = setTimeout(tick, servoRepeatMs);
|
||||
const latest = latestRef.current;
|
||||
if (!latest) {
|
||||
stopServoLoop();
|
||||
return;
|
||||
}
|
||||
latest.nudgeServo(nextDirection * latest.servoStep);
|
||||
servoIntervalRef.current = setTimeout(tick, latest.servoRepeatMs);
|
||||
};
|
||||
servoIntervalRef.current = setTimeout(tick, 0);
|
||||
}, [computeServoDirection, nudgeServo, servoRepeatMs, servoStep, stopServoLoop]);
|
||||
}, [computeServoDirection, stopServoLoop]);
|
||||
|
||||
const stopSongLoop = useCallback(() => {
|
||||
if (songIntervalRef.current) {
|
||||
@@ -214,27 +227,27 @@ export default function KeyboardInputManager() {
|
||||
}, []);
|
||||
|
||||
const computeSongDirection = useCallback(() => {
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return 0;
|
||||
const tokensSnapshot = new Set(activeTokensRef.current);
|
||||
const up = bindingActive(keymap.songNoteUp, tokensSnapshot);
|
||||
const down = bindingActive(keymap.songNoteDown, tokensSnapshot);
|
||||
const up = bindingActive(latest.keymap.songNoteUp, tokensSnapshot);
|
||||
const down = bindingActive(latest.keymap.songNoteDown, tokensSnapshot);
|
||||
return (up ? 1 : 0) - (down ? 1 : 0);
|
||||
}, [keymap]);
|
||||
}, []);
|
||||
|
||||
const triggerSongChange = useCallback(
|
||||
(direction) => {
|
||||
if (direction === 0) return;
|
||||
const current = typeof state.song?.note === 'number' ? state.song.note : SONG_DEFAULT_NOTE;
|
||||
let next = current + direction;
|
||||
if (next > NOTE_MAX) {
|
||||
next = NOTE_MIN;
|
||||
} else if (next < NOTE_MIN) {
|
||||
next = NOTE_MAX;
|
||||
}
|
||||
const finalNote = setSongNote(next);
|
||||
sendSong([{ note: finalNote, duration: SONG_DEFAULT_DURATION }], { slot: 0 });
|
||||
},
|
||||
[sendSong, setSongNote, state.song],
|
||||
);
|
||||
const triggerSongChange = useCallback((direction) => {
|
||||
const latest = latestRef.current;
|
||||
if (!latest || direction === 0) return;
|
||||
const current = typeof latest.songNote === 'number' ? latest.songNote : SONG_DEFAULT_NOTE;
|
||||
let next = current + direction;
|
||||
if (next > NOTE_MAX) {
|
||||
next = NOTE_MIN;
|
||||
} else if (next < NOTE_MIN) {
|
||||
next = NOTE_MAX;
|
||||
}
|
||||
const finalNote = latest.setSongNote(next);
|
||||
latest.sendSong([{ note: finalNote, duration: SONG_DEFAULT_DURATION }], { slot: 0 });
|
||||
}, []);
|
||||
|
||||
const ensureSongLoop = useCallback(() => {
|
||||
const direction = computeSongDirection();
|
||||
@@ -258,6 +271,7 @@ export default function KeyboardInputManager() {
|
||||
}, [computeSongDirection, stopSongLoop, triggerSongChange]);
|
||||
|
||||
const resetAll = useCallback(() => {
|
||||
const latest = latestRef.current;
|
||||
activeTokensRef.current.clear();
|
||||
lastVectorRef.current = ZERO_VECTOR;
|
||||
lastAuxRef.current = ZERO_AUX;
|
||||
@@ -265,97 +279,131 @@ export default function KeyboardInputManager() {
|
||||
stopSongLoop();
|
||||
if (hornActiveRef.current) {
|
||||
hornActiveRef.current = false;
|
||||
stopHorn();
|
||||
latest?.stopHorn();
|
||||
}
|
||||
setMicPttActive(false);
|
||||
stopAllMotion();
|
||||
registerInputState(SOURCE, { keys: [], vector: ZERO_VECTOR, aux: ZERO_AUX });
|
||||
}, [registerInputState, setMicPttActive, stopAllMotion, stopHorn, stopServoLoop, stopSongLoop]);
|
||||
latest?.setMicPttActive(false);
|
||||
latest?.stopAllMotion();
|
||||
latest?.registerInputState(SOURCE, { keys: [], vector: ZERO_VECTOR, aux: ZERO_AUX });
|
||||
}, [stopServoLoop, stopSongLoop]);
|
||||
|
||||
const triggerHomeAssistantCycle = useCallback(
|
||||
(targetState) => {
|
||||
const ha = homeAssistant;
|
||||
if (!ha?.enabled || !ha?.connected) return;
|
||||
if (ha?.lightPolicy?.locked || ha?.lightPolicy?.lockedOn) return;
|
||||
const entities = ha.entities || [];
|
||||
const eligible = entities.filter(
|
||||
(ent) =>
|
||||
(ent.type === 'light' || ent.type === 'switch') &&
|
||||
ent.available !== false &&
|
||||
ent.state !== 'unavailable',
|
||||
);
|
||||
if (eligible.length === 0) return;
|
||||
if (targetState === 'on') {
|
||||
const next = eligible.find((ent) => ent.state !== 'on');
|
||||
if (next) {
|
||||
homeAssistantSetState(next.id, 'on').catch(() => {});
|
||||
}
|
||||
return;
|
||||
const triggerHomeAssistantCycle = useCallback((targetState) => {
|
||||
const latest = latestRef.current;
|
||||
const ha = latest?.homeAssistant;
|
||||
if (!ha?.enabled || !ha?.connected) return;
|
||||
if (ha?.lightPolicy?.locked || ha?.lightPolicy?.lockedOn) return;
|
||||
const entities = ha.entities || [];
|
||||
const eligible = entities.filter(
|
||||
(ent) =>
|
||||
(ent.type === 'light' || ent.type === 'switch') &&
|
||||
ent.available !== false &&
|
||||
ent.state !== 'unavailable',
|
||||
);
|
||||
if (eligible.length === 0) return;
|
||||
if (targetState === 'on') {
|
||||
const next = eligible.find((ent) => ent.state !== 'on');
|
||||
if (next) {
|
||||
latest.homeAssistantSetState(next.id, 'on').catch(() => {});
|
||||
}
|
||||
if (targetState === 'off') {
|
||||
for (let idx = eligible.length - 1; idx >= 0; idx -= 1) {
|
||||
const ent = eligible[idx];
|
||||
if (ent.state === 'on') {
|
||||
homeAssistantSetState(ent.id, 'off').catch(() => {});
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (targetState === 'off') {
|
||||
for (let idx = eligible.length - 1; idx >= 0; idx -= 1) {
|
||||
const ent = eligible[idx];
|
||||
if (ent.state === 'on') {
|
||||
latest.homeAssistantSetState(ent.id, 'off').catch(() => {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[homeAssistantSetState, homeAssistant],
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const cycleVideoFilter = useCallback(() => {
|
||||
// Update through the same settings namespace as the Page settings dropdown so the keyboard
|
||||
// shortcut and menu never maintain separate copies of the selected filter.
|
||||
saveVideoSettings((current) => ({
|
||||
const latest = latestRef.current;
|
||||
latest?.saveVideoSettings((current) => ({
|
||||
...(current ?? {}),
|
||||
colorFilter: nextVideoFilter(current?.colorFilter),
|
||||
}));
|
||||
}, [saveVideoSettings]);
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
// The assignment lives in a layout effect instead of render because the current React lint
|
||||
// rules reserve refs for effects and event handlers. Layout timing keeps the ref current
|
||||
// before the browser can deliver the next keyboard event after a committed render.
|
||||
latestRef.current = {
|
||||
actionTokens,
|
||||
dockAssist,
|
||||
focusChat,
|
||||
homeAssistant,
|
||||
homeAssistantSetState,
|
||||
isChatFocused,
|
||||
keyboardSpeeds,
|
||||
keymap,
|
||||
nudgeServo,
|
||||
registerInputState,
|
||||
runMacro,
|
||||
saveVideoSettings,
|
||||
sendSong,
|
||||
servoRepeatMs,
|
||||
servoStep,
|
||||
setAuxMotors,
|
||||
setDriveVector,
|
||||
setMicPttActive,
|
||||
setMode,
|
||||
setSongNote,
|
||||
songNote: state.song?.note,
|
||||
startHorn,
|
||||
stopAllMotion,
|
||||
stopHorn,
|
||||
toggleNightVision,
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyDown(event) {
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return;
|
||||
if (isKeyboardCaptureLocked()) return;
|
||||
if (shouldIgnoreEvent(event)) return;
|
||||
const tokens = tokensForEvent(event);
|
||||
if (tokens.length === 0) return;
|
||||
const tokenSet = new Set(tokens);
|
||||
if (bindingActive(keymap.chatFocus, tokenSet)) {
|
||||
if (bindingActive(latest.keymap.chatFocus, tokenSet)) {
|
||||
event.preventDefault();
|
||||
resetAll();
|
||||
if (!isChatFocused) {
|
||||
focusChat();
|
||||
if (!latest.isChatFocused) {
|
||||
latest.focusChat();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (tokens.some((token) => actionTokens.has(token))) {
|
||||
if (tokens.some((token) => latest.actionTokens.has(token))) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const newlyPressed = tokens.filter((token) => !activeTokensRef.current.has(token));
|
||||
newlyPressed.forEach((token) => activeTokensRef.current.add(token));
|
||||
|
||||
if (newlyPressed.length > 0) {
|
||||
if (newlyPressed.some((token) => keymap.driveMacro?.has(token))) {
|
||||
dockAssist.exitAssist();
|
||||
setMode('drive');
|
||||
runMacro('drive-sequence');
|
||||
} else if (newlyPressed.some((token) => keymap.dockMacro?.has(token))) {
|
||||
dockAssist.toggleAssist();
|
||||
} else if (newlyPressed.some((token) => keymap.nightVisionToggle?.has(token))) {
|
||||
toggleNightVision();
|
||||
} else if (newlyPressed.some((token) => keymap.videoFilterCycle?.has(token))) {
|
||||
if (newlyPressed.some((token) => latest.keymap.driveMacro?.has(token))) {
|
||||
latest.dockAssist.exitAssist();
|
||||
latest.setMode('drive');
|
||||
latest.runMacro('drive-sequence');
|
||||
} else if (newlyPressed.some((token) => latest.keymap.dockMacro?.has(token))) {
|
||||
latest.dockAssist.toggleAssist();
|
||||
} else if (newlyPressed.some((token) => latest.keymap.nightVisionToggle?.has(token))) {
|
||||
latest.toggleNightVision();
|
||||
} else if (newlyPressed.some((token) => latest.keymap.videoFilterCycle?.has(token))) {
|
||||
cycleVideoFilter();
|
||||
} else if (newlyPressed.some((token) => keymap.hornHonk?.has(token))) {
|
||||
} else if (newlyPressed.some((token) => latest.keymap.hornHonk?.has(token))) {
|
||||
if (!hornActiveRef.current) {
|
||||
const started = startHorn();
|
||||
const started = latest.startHorn();
|
||||
hornActiveRef.current = Boolean(started);
|
||||
}
|
||||
} else if (newlyPressed.some((token) => keymap.micPtt?.has(token))) {
|
||||
setMicPttActive(true);
|
||||
} else if (newlyPressed.some((token) => keymap.homeAssistantOn?.has(token))) {
|
||||
} else if (newlyPressed.some((token) => latest.keymap.micPtt?.has(token))) {
|
||||
latest.setMicPttActive(true);
|
||||
} else if (newlyPressed.some((token) => latest.keymap.homeAssistantOn?.has(token))) {
|
||||
triggerHomeAssistantCycle('on');
|
||||
} else if (newlyPressed.some((token) => keymap.homeAssistantOff?.has(token))) {
|
||||
} else if (newlyPressed.some((token) => latest.keymap.homeAssistantOff?.has(token))) {
|
||||
triggerHomeAssistantCycle('off');
|
||||
}
|
||||
}
|
||||
@@ -366,15 +414,17 @@ export default function KeyboardInputManager() {
|
||||
}
|
||||
|
||||
function handleKeyUp(event) {
|
||||
const latest = latestRef.current;
|
||||
if (!latest) return;
|
||||
if (isKeyboardCaptureLocked()) return;
|
||||
const tokens = tokensForEvent(event);
|
||||
tokens.forEach((token) => activeTokensRef.current.delete(token));
|
||||
if (hornActiveRef.current && !bindingActive(keymap.hornHonk, activeTokensRef.current)) {
|
||||
if (hornActiveRef.current && !bindingActive(latest.keymap.hornHonk, activeTokensRef.current)) {
|
||||
hornActiveRef.current = false;
|
||||
stopHorn();
|
||||
latest.stopHorn();
|
||||
}
|
||||
if (!bindingActive(keymap.micPtt, activeTokensRef.current)) {
|
||||
setMicPttActive(false);
|
||||
if (!bindingActive(latest.keymap.micPtt, activeTokensRef.current)) {
|
||||
latest.setMicPttActive(false);
|
||||
}
|
||||
ensureServoLoop();
|
||||
ensureSongLoop();
|
||||
@@ -385,6 +435,9 @@ export default function KeyboardInputManager() {
|
||||
resetAll();
|
||||
}
|
||||
|
||||
// The global listeners are intentionally registered through stable handlers. High-frequency
|
||||
// control context updates should refresh latestRef.current, not force the browser to detach
|
||||
// and reattach window listeners while someone may be driving.
|
||||
window.addEventListener('keydown', handleKeyDown, { capture: true });
|
||||
window.addEventListener('keyup', handleKeyUp, { capture: true });
|
||||
window.addEventListener('blur', handleBlur);
|
||||
@@ -393,36 +446,7 @@ export default function KeyboardInputManager() {
|
||||
window.removeEventListener('keyup', handleKeyUp, { capture: true });
|
||||
window.removeEventListener('blur', handleBlur);
|
||||
};
|
||||
}, [
|
||||
actionTokens,
|
||||
blurChat,
|
||||
driveFromKeys,
|
||||
ensureServoLoop,
|
||||
ensureSongLoop,
|
||||
focusChat,
|
||||
isChatFocused,
|
||||
keymap.chatFocus,
|
||||
keymap.dockMacro,
|
||||
keymap.driveMacro,
|
||||
keymap.hornHonk,
|
||||
keymap.homeAssistantOff,
|
||||
keymap.homeAssistantOn,
|
||||
keymap.micPtt,
|
||||
keymap.nightVisionToggle,
|
||||
keymap.videoFilterCycle,
|
||||
resetAll,
|
||||
runMacro,
|
||||
setMicPttActive,
|
||||
setMode,
|
||||
stopAllMotion,
|
||||
stopSongLoop,
|
||||
startHorn,
|
||||
stopHorn,
|
||||
toggleNightVision,
|
||||
triggerHomeAssistantCycle,
|
||||
cycleVideoFilter,
|
||||
dockAssist,
|
||||
]);
|
||||
}, [cycleVideoFilter, driveFromKeys, ensureServoLoop, ensureSongLoop, resetAll, triggerHomeAssistantCycle]);
|
||||
|
||||
const latestResetAllRef = useRef(resetAll);
|
||||
useEffect(() => {
|
||||
|
||||