@grafana/scenes 6.35.2--canary.1148.17723712400.0 → 6.35.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1318,6 +1318,13 @@ declare class UserActionEvent extends BusEventWithPayload<UserActionEventPayload
1318
1318
  */
1319
1319
  declare function lookupVariable(name: string, sceneObject: SceneObject): SceneVariable | null;
1320
1320
 
1321
+ interface LongFrameEvent {
1322
+ duration: number;
1323
+ timestamp: number;
1324
+ method: 'manual' | 'loaf';
1325
+ }
1326
+ type LongFrameCallback = (event: LongFrameEvent) => void;
1327
+
1321
1328
  interface QueryResultWithState {
1322
1329
  state: LoadingState;
1323
1330
  }
@@ -1328,6 +1335,9 @@ interface SceneQueryControllerEntry {
1328
1335
  cancel?: () => void;
1329
1336
  }
1330
1337
  type SceneQueryControllerEntryType = 'data' | 'annotations' | 'variable' | 'alerts' | 'plugin' | string;
1338
+
1339
+
1340
+
1331
1341
  interface SceneInteractionProfileEvent {
1332
1342
  origin: string;
1333
1343
  duration: number;
@@ -1338,6 +1348,8 @@ interface SceneInteractionProfileEvent {
1338
1348
  crumbs: string[];
1339
1349
  startTs: number;
1340
1350
  endTs: number;
1351
+ longFramesCount: number;
1352
+ longFramesTotalTime: number;
1341
1353
  }
1342
1354
  interface SceneQueryStateControllerState extends SceneObjectState {
1343
1355
  isRunning: boolean;
@@ -1533,6 +1545,23 @@ declare class PanelContextEventBus implements EventBus {
1533
1545
  newScopedBus(key: string, filter: EventFilterOptions): EventBus;
1534
1546
  }
1535
1547
 
1548
+ /**
1549
+ * SceneRenderProfiler tracks dashboard interaction performance including:
1550
+ * - Total interaction duration
1551
+ * - Network time
1552
+ * - Long frame detection (50ms threshold) during interaction using LoAF API (default) or manual tracking (fallback)
1553
+ * - Slow frame detection (30ms threshold) for tail recording after interaction
1554
+ *
1555
+ * Long frame detection during interaction:
1556
+ * - 50ms threshold aligned with LoAF API default
1557
+ * - LoAF API preferred (Chrome 123+) with manual fallback
1558
+ * - Provides script attribution when using LoAF API
1559
+ *
1560
+ * Slow frame detection for tail recording:
1561
+ * - 30ms threshold for post-interaction monitoring
1562
+ * - Manual frame timing measurement
1563
+ * - Captures rendering delays after user interaction completes
1564
+ */
1536
1565
  declare class SceneRenderProfiler {
1537
1566
  #private;
1538
1567
  private queryController?;
package/dist/index.js CHANGED
@@ -137,21 +137,21 @@ class UserActionEvent extends data.BusEventWithPayload {
137
137
  }
138
138
  UserActionEvent.type = "scene-object-user-action";
139
139
 
140
- var __typeError$4 = (msg) => {
140
+ var __typeError$5 = (msg) => {
141
141
  throw TypeError(msg);
142
142
  };
143
- var __accessCheck$4 = (obj, member, msg) => member.has(obj) || __typeError$4("Cannot " + msg);
144
- var __privateGet$4 = (obj, member, getter) => (__accessCheck$4(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
145
- var __privateAdd$4 = (obj, member, value) => member.has(obj) ? __typeError$4("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
146
- var __privateSet$4 = (obj, member, value, setter) => (__accessCheck$4(obj, member, "write to private field"), member.set(obj, value), value);
143
+ var __accessCheck$5 = (obj, member, msg) => member.has(obj) || __typeError$5("Cannot " + msg);
144
+ var __privateGet$5 = (obj, member, getter) => (__accessCheck$5(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
145
+ var __privateAdd$5 = (obj, member, value) => member.has(obj) ? __typeError$5("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
146
+ var __privateSet$5 = (obj, member, value, setter) => (__accessCheck$5(obj, member, "write to private field"), member.set(obj, value), value);
147
147
  var _ref;
148
148
  class SceneObjectRef {
149
149
  constructor(ref) {
150
- __privateAdd$4(this, _ref);
151
- __privateSet$4(this, _ref, ref);
150
+ __privateAdd$5(this, _ref);
151
+ __privateSet$5(this, _ref, ref);
152
152
  }
153
153
  resolve() {
154
- return __privateGet$4(this, _ref);
154
+ return __privateGet$5(this, _ref);
155
155
  }
156
156
  }
157
157
  _ref = new WeakMap();
@@ -599,14 +599,23 @@ function writeSceneLog(logger, message, ...rest) {
599
599
  console.log(`${logger}: `, message, ...rest);
600
600
  }
601
601
  }
602
+ function writeSceneLogStyled(logger, message, style, ...rest) {
603
+ let loggingEnabled = false;
604
+ if (typeof window !== "undefined") {
605
+ loggingEnabled = localStorage.getItem("grafana.debug.scenes") === "true";
606
+ }
607
+ if (loggingEnabled) {
608
+ console.log(`%c${logger}: ${message}`, style, ...rest);
609
+ }
610
+ }
602
611
 
603
- var __typeError$3 = (msg) => {
612
+ var __typeError$4 = (msg) => {
604
613
  throw TypeError(msg);
605
614
  };
606
- var __accessCheck$3 = (obj, member, msg) => member.has(obj) || __typeError$3("Cannot " + msg);
607
- var __privateGet$3 = (obj, member, getter) => (__accessCheck$3(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
608
- var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$3("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
609
- var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), member.set(obj, value), value);
615
+ var __accessCheck$4 = (obj, member, msg) => member.has(obj) || __typeError$4("Cannot " + msg);
616
+ var __privateGet$4 = (obj, member, getter) => (__accessCheck$4(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
617
+ var __privateAdd$4 = (obj, member, value) => member.has(obj) ? __typeError$4("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
618
+ var __privateSet$4 = (obj, member, value, setter) => (__accessCheck$4(obj, member, "write to private field"), member.set(obj, value), value);
610
619
  var _running, _tryCompleteProfileFrameId;
611
620
  function isQueryController(s) {
612
621
  return "isQueryController" in s;
@@ -616,10 +625,10 @@ class SceneQueryController extends SceneObjectBase {
616
625
  super({ ...state, isRunning: false });
617
626
  this.profiler = profiler;
618
627
  this.isQueryController = true;
619
- __privateAdd$3(this, _running, /* @__PURE__ */ new Set());
620
- __privateAdd$3(this, _tryCompleteProfileFrameId, null);
628
+ __privateAdd$4(this, _running, /* @__PURE__ */ new Set());
629
+ __privateAdd$4(this, _tryCompleteProfileFrameId, null);
621
630
  this.runningQueriesCount = () => {
622
- return __privateGet$3(this, _running).size;
631
+ return __privateGet$4(this, _running).size;
623
632
  };
624
633
  if (profiler) {
625
634
  this.profiler = profiler;
@@ -628,7 +637,7 @@ class SceneQueryController extends SceneObjectBase {
628
637
  this.addActivationHandler(() => {
629
638
  var _a;
630
639
  (_a = this.profiler) == null ? void 0 : _a.setQueryController(this);
631
- return () => __privateGet$3(this, _running).clear();
640
+ return () => __privateGet$4(this, _running).clear();
632
641
  });
633
642
  }
634
643
  startProfile(name) {
@@ -643,19 +652,19 @@ class SceneQueryController extends SceneObjectBase {
643
652
  (_a = this.profiler) == null ? void 0 : _a.cancelProfile();
644
653
  }
645
654
  queryStarted(entry) {
646
- __privateGet$3(this, _running).add(entry);
655
+ __privateGet$4(this, _running).add(entry);
647
656
  this.changeRunningQueryCount(1, entry);
648
657
  if (!this.state.isRunning) {
649
658
  this.setState({ isRunning: true });
650
659
  }
651
660
  }
652
661
  queryCompleted(entry) {
653
- if (!__privateGet$3(this, _running).has(entry)) {
662
+ if (!__privateGet$4(this, _running).has(entry)) {
654
663
  return;
655
664
  }
656
- __privateGet$3(this, _running).delete(entry);
665
+ __privateGet$4(this, _running).delete(entry);
657
666
  this.changeRunningQueryCount(-1);
658
- if (__privateGet$3(this, _running).size === 0) {
667
+ if (__privateGet$4(this, _running).size === 0) {
659
668
  this.setState({ isRunning: false });
660
669
  }
661
670
  }
@@ -672,10 +681,10 @@ class SceneQueryController extends SceneObjectBase {
672
681
  }
673
682
  }
674
683
  if (this.state.enableProfiling) {
675
- if (__privateGet$3(this, _tryCompleteProfileFrameId)) {
676
- cancelAnimationFrame(__privateGet$3(this, _tryCompleteProfileFrameId));
684
+ if (__privateGet$4(this, _tryCompleteProfileFrameId)) {
685
+ cancelAnimationFrame(__privateGet$4(this, _tryCompleteProfileFrameId));
677
686
  }
678
- __privateSet$3(this, _tryCompleteProfileFrameId, requestAnimationFrame(() => {
687
+ __privateSet$4(this, _tryCompleteProfileFrameId, requestAnimationFrame(() => {
679
688
  var _a2;
680
689
  (_a2 = this.profiler) == null ? void 0 : _a2.tryCompletingProfile();
681
690
  }));
@@ -683,7 +692,7 @@ class SceneQueryController extends SceneObjectBase {
683
692
  }
684
693
  cancelAll() {
685
694
  var _a;
686
- for (const entry of __privateGet$3(this, _running).values()) {
695
+ for (const entry of __privateGet$4(this, _running).values()) {
687
696
  (_a = entry.cancel) == null ? void 0 : _a.call(entry);
688
697
  }
689
698
  }
@@ -853,6 +862,187 @@ function isValid$1(value, roundUp, timeZone) {
853
862
  return parsed.isValid();
854
863
  }
855
864
 
865
+ var __typeError$3 = (msg) => {
866
+ throw TypeError(msg);
867
+ };
868
+ var __accessCheck$3 = (obj, member, msg) => member.has(obj) || __typeError$3("Cannot " + msg);
869
+ var __privateGet$3 = (obj, member, getter) => (__accessCheck$3(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
870
+ var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$3("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
871
+ var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), member.set(obj, value), value);
872
+ var _isTracking, _callback, _frameTrackingId, _lastFrameTime, _loafObserver;
873
+ const LONG_FRAME_THRESHOLD = 50;
874
+ class LongFrameDetector {
875
+ constructor() {
876
+ __privateAdd$3(this, _isTracking, false);
877
+ __privateAdd$3(this, _callback, null);
878
+ // Manual tracking state
879
+ __privateAdd$3(this, _frameTrackingId, null);
880
+ __privateAdd$3(this, _lastFrameTime, 0);
881
+ // LoAF tracking state
882
+ __privateAdd$3(this, _loafObserver, null);
883
+ /**
884
+ * Measure frame durations using requestAnimationFrame
885
+ */
886
+ this.measureFrames = () => {
887
+ if (!__privateGet$3(this, _isTracking)) {
888
+ return;
889
+ }
890
+ const currentFrameTime = performance.now();
891
+ const frameLength = currentFrameTime - __privateGet$3(this, _lastFrameTime);
892
+ if (frameLength > LONG_FRAME_THRESHOLD) {
893
+ const event = {
894
+ duration: frameLength,
895
+ timestamp: currentFrameTime,
896
+ method: "manual"
897
+ };
898
+ if (__privateGet$3(this, _callback)) {
899
+ __privateGet$3(this, _callback).call(this, event);
900
+ }
901
+ if (typeof performance !== "undefined" && performance.mark && performance.measure) {
902
+ const frameId = `long-frame-manual-${currentFrameTime.toFixed(0)}`;
903
+ const startMarkName = `${frameId}-start`;
904
+ const endMarkName = `${frameId}-end`;
905
+ const measureName = `Long Frame (Manual): ${frameLength.toFixed(1)}ms`;
906
+ try {
907
+ performance.mark(startMarkName, { startTime: currentFrameTime - frameLength });
908
+ performance.mark(endMarkName, { startTime: currentFrameTime });
909
+ performance.measure(measureName, startMarkName, endMarkName);
910
+ } catch (e) {
911
+ performance.mark(measureName);
912
+ }
913
+ }
914
+ writeSceneLog(
915
+ "LongFrameDetector",
916
+ `Long frame detected (manual): ${frameLength}ms (threshold: ${LONG_FRAME_THRESHOLD}ms)`
917
+ );
918
+ }
919
+ __privateSet$3(this, _lastFrameTime, currentFrameTime);
920
+ if (__privateGet$3(this, _isTracking)) {
921
+ __privateSet$3(this, _frameTrackingId, requestAnimationFrame(this.measureFrames));
922
+ }
923
+ };
924
+ }
925
+ /**
926
+ * Check if LoAF API is available in the browser
927
+ */
928
+ isLoAFAvailable() {
929
+ return typeof PerformanceObserver !== "undefined" && PerformanceObserver.supportedEntryTypes && PerformanceObserver.supportedEntryTypes.includes("long-animation-frame");
930
+ }
931
+ /**
932
+ * Start detecting long frames and call the provided callback when they occur
933
+ */
934
+ start(callback) {
935
+ if (__privateGet$3(this, _isTracking)) {
936
+ writeSceneLog("LongFrameDetector", "Already tracking frames, stopping previous session");
937
+ this.stop();
938
+ }
939
+ __privateSet$3(this, _callback, callback);
940
+ __privateSet$3(this, _isTracking, true);
941
+ if (this.isLoAFAvailable()) {
942
+ this.startLoAFTracking();
943
+ } else {
944
+ this.startManualFrameTracking();
945
+ }
946
+ writeSceneLog(
947
+ "LongFrameDetector",
948
+ `Started tracking with ${this.isLoAFAvailable() ? "LoAF API" : "manual"} method, threshold: ${LONG_FRAME_THRESHOLD}ms`
949
+ );
950
+ }
951
+ /**
952
+ * Stop detecting long frames
953
+ */
954
+ stop() {
955
+ if (!__privateGet$3(this, _isTracking)) {
956
+ return;
957
+ }
958
+ __privateSet$3(this, _isTracking, false);
959
+ __privateSet$3(this, _callback, null);
960
+ this.stopLoAFTracking();
961
+ this.stopManualFrameTracking();
962
+ }
963
+ /**
964
+ * Check if currently tracking frames
965
+ */
966
+ isTracking() {
967
+ return __privateGet$3(this, _isTracking);
968
+ }
969
+ /**
970
+ * Start tracking using the Long Animation Frame API
971
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceLongAnimationFrameTiming
972
+ */
973
+ startLoAFTracking() {
974
+ if (!this.isLoAFAvailable()) {
975
+ writeSceneLog("LongFrameDetector", "LoAF API not available, falling back to manual tracking");
976
+ this.startManualFrameTracking();
977
+ return;
978
+ }
979
+ try {
980
+ __privateSet$3(this, _loafObserver, new PerformanceObserver((list) => {
981
+ for (const entry of list.getEntries()) {
982
+ const event = {
983
+ duration: entry.duration,
984
+ timestamp: entry.startTime,
985
+ method: "loaf"
986
+ };
987
+ if (__privateGet$3(this, _callback)) {
988
+ __privateGet$3(this, _callback).call(this, event);
989
+ }
990
+ if (typeof performance !== "undefined" && performance.mark && performance.measure) {
991
+ const frameId = `long-frame-${entry.startTime.toFixed(0)}`;
992
+ const startMarkName = `${frameId}-start`;
993
+ const endMarkName = `${frameId}-end`;
994
+ const measureName = `Long Frame (LoAF): ${entry.duration.toFixed(1)}ms`;
995
+ try {
996
+ performance.mark(startMarkName, { startTime: entry.startTime });
997
+ performance.mark(endMarkName, { startTime: entry.startTime + entry.duration });
998
+ performance.measure(measureName, startMarkName, endMarkName);
999
+ } catch (e) {
1000
+ performance.mark(measureName);
1001
+ }
1002
+ }
1003
+ writeSceneLog("LongFrameDetector", `Long frame detected (LoAF): ${entry.duration}ms at ${entry.startTime}ms`);
1004
+ }
1005
+ }));
1006
+ __privateGet$3(this, _loafObserver).observe({ type: "long-animation-frame", buffered: false });
1007
+ } catch (error) {
1008
+ writeSceneLog("LongFrameDetector", "Failed to start LoAF tracking, falling back to manual:", error);
1009
+ this.startManualFrameTracking();
1010
+ }
1011
+ }
1012
+ /**
1013
+ * Stop LoAF tracking
1014
+ */
1015
+ stopLoAFTracking() {
1016
+ if (__privateGet$3(this, _loafObserver)) {
1017
+ __privateGet$3(this, _loafObserver).disconnect();
1018
+ __privateSet$3(this, _loafObserver, null);
1019
+ writeSceneLog("LongFrameDetector", "Stopped LoAF tracking");
1020
+ }
1021
+ }
1022
+ /**
1023
+ * Start manual frame tracking using requestAnimationFrame
1024
+ */
1025
+ startManualFrameTracking() {
1026
+ __privateSet$3(this, _lastFrameTime, performance.now());
1027
+ __privateSet$3(this, _frameTrackingId, requestAnimationFrame(() => this.measureFrames()));
1028
+ }
1029
+ /**
1030
+ * Stop manual frame tracking
1031
+ */
1032
+ stopManualFrameTracking() {
1033
+ if (__privateGet$3(this, _frameTrackingId)) {
1034
+ cancelAnimationFrame(__privateGet$3(this, _frameTrackingId));
1035
+ __privateSet$3(this, _frameTrackingId, null);
1036
+ writeSceneLog("LongFrameDetector", "Stopped manual frame tracking");
1037
+ }
1038
+ }
1039
+ }
1040
+ _isTracking = new WeakMap();
1041
+ _callback = new WeakMap();
1042
+ _frameTrackingId = new WeakMap();
1043
+ _lastFrameTime = new WeakMap();
1044
+ _loafObserver = new WeakMap();
1045
+
856
1046
  var __typeError$2 = (msg) => {
857
1047
  throw TypeError(msg);
858
1048
  };
@@ -860,9 +1050,17 @@ var __accessCheck$2 = (obj, member, msg) => member.has(obj) || __typeError$2("Ca
860
1050
  var __privateGet$2 = (obj, member, getter) => (__accessCheck$2(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
861
1051
  var __privateAdd$2 = (obj, member, value) => member.has(obj) ? __typeError$2("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
862
1052
  var __privateSet$2 = (obj, member, value, setter) => (__accessCheck$2(obj, member, "write to private field"), member.set(obj, value), value);
863
- var _profileInProgress, _profileStartTs, _trailAnimationFrameId, _recordedTrailingSpans, _visibilityChangeHandler;
1053
+ var __privateWrapper = (obj, member, setter, getter) => ({
1054
+ set _(value) {
1055
+ __privateSet$2(obj, member, value);
1056
+ },
1057
+ get _() {
1058
+ return __privateGet$2(obj, member, getter);
1059
+ }
1060
+ });
1061
+ var _profileInProgress, _profileStartTs, _trailAnimationFrameId, _recordedTrailingSpans, _longFrameDetector, _longFramesCount, _longFramesTotalTime, _visibilityChangeHandler;
864
1062
  const POST_STORM_WINDOW = 2e3;
865
- const SPAN_THRESHOLD = 30;
1063
+ const DEFAULT_LONG_FRAME_THRESHOLD = 30;
866
1064
  const TAB_INACTIVE_THRESHOLD = 1e3;
867
1065
  class SceneRenderProfiler {
868
1066
  constructor(queryController) {
@@ -872,9 +1070,13 @@ class SceneRenderProfiler {
872
1070
  __privateAdd$2(this, _trailAnimationFrameId, null);
873
1071
  // Will keep measured lengths trailing frames
874
1072
  __privateAdd$2(this, _recordedTrailingSpans, []);
1073
+ // Long frame tracking
1074
+ __privateAdd$2(this, _longFrameDetector);
1075
+ __privateAdd$2(this, _longFramesCount, 0);
1076
+ __privateAdd$2(this, _longFramesTotalTime, 0);
875
1077
  __privateAdd$2(this, _visibilityChangeHandler, null);
876
1078
  this.measureTrailingFrames = (measurementStartTs, lastFrameTime, profileStartTs) => {
877
- var _a;
1079
+ var _a, _b, _c, _d;
878
1080
  const currentFrameTime = performance.now();
879
1081
  const frameLength = currentFrameTime - lastFrameTime;
880
1082
  if (frameLength > TAB_INACTIVE_THRESHOLD) {
@@ -893,18 +1095,60 @@ class SceneRenderProfiler {
893
1095
  const slowFrames = processRecordedSpans(__privateGet$2(this, _recordedTrailingSpans));
894
1096
  const slowFramesTime = slowFrames.reduce((acc, val) => acc + val, 0);
895
1097
  writeSceneLog(
896
- this.constructor.name,
897
- "Profile tail recorded, slow frames duration:",
898
- slowFramesTime,
899
- slowFrames,
900
- __privateGet$2(this, _profileInProgress)
1098
+ "SceneRenderProfiler",
1099
+ `Profile tail recorded - Slow frames: ${slowFramesTime.toFixed(1)}ms (${slowFrames.length} frames)`
901
1100
  );
1101
+ writeSceneLog("", ` \u251C\u2500 Origin: ${((_a = __privateGet$2(this, _profileInProgress)) == null ? void 0 : _a.origin) || "unknown"}`);
1102
+ writeSceneLog("", ` \u2514\u2500 Crumbs:`, ((_b = __privateGet$2(this, _profileInProgress)) == null ? void 0 : _b.crumbs) || []);
902
1103
  __privateSet$2(this, _recordedTrailingSpans, []);
903
1104
  const profileDuration = measurementStartTs - profileStartTs;
904
- writeSceneLog(
905
- this.constructor.name,
906
- "Stoped recording, total measured time (network included):",
907
- profileDuration + slowFramesTime
1105
+ if (typeof performance !== "undefined" && performance.mark) {
1106
+ const profileName = ((_c = __privateGet$2(this, _profileInProgress)) == null ? void 0 : _c.origin) || "unknown";
1107
+ const totalTime = profileDuration + slowFramesTime;
1108
+ performance.mark(`Dashboard Profile End: ${profileName}`);
1109
+ const startMarkName = `Dashboard Profile Start: ${profileName}`;
1110
+ try {
1111
+ performance.measure(
1112
+ `Dashboard Profile: ${profileName} (${totalTime.toFixed(1)}ms)`,
1113
+ startMarkName,
1114
+ `Dashboard Profile End: ${profileName}`
1115
+ );
1116
+ } catch (e) {
1117
+ performance.mark(`Dashboard Profile Complete: ${profileName} (${totalTime.toFixed(1)}ms)`);
1118
+ }
1119
+ if (slowFrames.length > 0) {
1120
+ const slowFramesMarkName = `Slow Frames Summary: ${slowFrames.length} frames (${slowFramesTime.toFixed(
1121
+ 1
1122
+ )}ms)`;
1123
+ performance.mark(slowFramesMarkName);
1124
+ slowFrames.forEach((frameTime, index) => {
1125
+ if (frameTime > 16) {
1126
+ try {
1127
+ const frameStartTime = __privateGet$2(this, _profileStartTs) + profileDuration + (index > 0 ? slowFrames.slice(0, index).reduce((sum, t) => sum + t, 0) : 0);
1128
+ const frameId = `slow-frame-${index}`;
1129
+ const frameStartMark = `${frameId}-start`;
1130
+ const frameEndMark = `${frameId}-end`;
1131
+ performance.mark(frameStartMark, { startTime: frameStartTime });
1132
+ performance.mark(frameEndMark, { startTime: frameStartTime + frameTime });
1133
+ performance.measure(`Slow Frame ${index + 1}: ${frameTime.toFixed(1)}ms`, frameStartMark, frameEndMark);
1134
+ } catch (e) {
1135
+ performance.mark(`Slow Frame ${index + 1}: ${frameTime.toFixed(1)}ms`);
1136
+ }
1137
+ }
1138
+ });
1139
+ }
1140
+ }
1141
+ const completionTimestamp = performance.now();
1142
+ writeSceneLog("SceneRenderProfiler", "Profile completed");
1143
+ writeSceneLog("", ` \u251C\u2500 Timestamp: ${completionTimestamp.toFixed(1)}ms`);
1144
+ writeSceneLog("", ` \u251C\u2500 Total time: ${(profileDuration + slowFramesTime).toFixed(1)}ms`);
1145
+ writeSceneLog("", ` \u251C\u2500 Slow frames: ${slowFramesTime}ms (${slowFrames.length} frames)`);
1146
+ writeSceneLog("", ` \u2514\u2500 Long frames: ${__privateGet$2(this, _longFramesTotalTime)}ms (${__privateGet$2(this, _longFramesCount)} frames)`);
1147
+ __privateGet$2(this, _longFrameDetector).stop();
1148
+ writeSceneLogStyled(
1149
+ "SceneRenderProfiler",
1150
+ `Stopped long frame detection - profile complete at ${completionTimestamp.toFixed(1)}ms`,
1151
+ "color: #00CC00; font-weight: bold;"
908
1152
  );
909
1153
  __privateSet$2(this, _trailAnimationFrameId, null);
910
1154
  const profileEndTs = profileStartTs + profileDuration + slowFramesTime;
@@ -916,7 +1160,7 @@ class SceneRenderProfiler {
916
1160
  end: profileEndTs
917
1161
  });
918
1162
  const networkDuration = captureNetwork(profileStartTs, profileEndTs);
919
- if (((_a = this.queryController) == null ? void 0 : _a.state.onProfileComplete) && __privateGet$2(this, _profileInProgress)) {
1163
+ if (((_d = this.queryController) == null ? void 0 : _d.state.onProfileComplete) && __privateGet$2(this, _profileInProgress)) {
920
1164
  this.queryController.state.onProfileComplete({
921
1165
  origin: __privateGet$2(this, _profileInProgress).origin,
922
1166
  crumbs: __privateGet$2(this, _profileInProgress).crumbs,
@@ -924,6 +1168,8 @@ class SceneRenderProfiler {
924
1168
  networkDuration,
925
1169
  startTs: profileStartTs,
926
1170
  endTs: profileEndTs,
1171
+ longFramesCount: __privateGet$2(this, _longFramesCount),
1172
+ longFramesTotalTime: __privateGet$2(this, _longFramesTotalTime),
927
1173
  // @ts-ignore
928
1174
  jsHeapSizeLimit: performance.memory ? performance.memory.jsHeapSizeLimit : 0,
929
1175
  // @ts-ignore
@@ -943,6 +1189,7 @@ class SceneRenderProfiler {
943
1189
  }
944
1190
  }
945
1191
  };
1192
+ __privateSet$2(this, _longFrameDetector, new LongFrameDetector());
946
1193
  this.setupVisibilityChangeHandler();
947
1194
  }
948
1195
  setQueryController(queryController) {
@@ -967,6 +1214,7 @@ class SceneRenderProfiler {
967
1214
  document.removeEventListener("visibilitychange", __privateGet$2(this, _visibilityChangeHandler));
968
1215
  __privateSet$2(this, _visibilityChangeHandler, null);
969
1216
  }
1217
+ __privateGet$2(this, _longFrameDetector).stop();
970
1218
  this.cancelProfile();
971
1219
  }
972
1220
  startProfile(name) {
@@ -996,14 +1244,32 @@ class SceneRenderProfiler {
996
1244
  * - "clean": Started when no profile is currently active
997
1245
  */
998
1246
  _startNewProfile(name, force = false) {
1247
+ var _a;
999
1248
  __privateSet$2(this, _profileInProgress, { origin: name, crumbs: [] });
1000
1249
  __privateSet$2(this, _profileStartTs, performance.now());
1001
- writeSceneLog(
1250
+ __privateSet$2(this, _longFramesCount, 0);
1251
+ __privateSet$2(this, _longFramesTotalTime, 0);
1252
+ if (typeof performance !== "undefined" && performance.mark) {
1253
+ const markName = `Dashboard Profile Start: ${name}`;
1254
+ performance.mark(markName);
1255
+ }
1256
+ writeSceneLogStyled(
1002
1257
  "SceneRenderProfiler",
1003
1258
  `Profile started[${force ? "forced" : "clean"}]`,
1004
- __privateGet$2(this, _profileInProgress),
1005
- __privateGet$2(this, _profileStartTs)
1259
+ "color: #FFCC00; font-weight: bold;"
1006
1260
  );
1261
+ writeSceneLog("", ` \u251C\u2500 Origin: ${((_a = __privateGet$2(this, _profileInProgress)) == null ? void 0 : _a.origin) || "unknown"}`);
1262
+ writeSceneLog("", ` \u2514\u2500 Timestamp: ${__privateGet$2(this, _profileStartTs).toFixed(1)}ms`);
1263
+ __privateGet$2(this, _longFrameDetector).start((event) => {
1264
+ if (!__privateGet$2(this, _profileInProgress) || !__privateGet$2(this, _profileStartTs)) {
1265
+ return;
1266
+ }
1267
+ if (event.timestamp < __privateGet$2(this, _profileStartTs)) {
1268
+ return;
1269
+ }
1270
+ __privateWrapper(this, _longFramesCount)._++;
1271
+ __privateSet$2(this, _longFramesTotalTime, __privateGet$2(this, _longFramesTotalTime) + event.duration);
1272
+ });
1007
1273
  }
1008
1274
  recordProfileTail(measurementStartTime, profileStartTs) {
1009
1275
  __privateSet$2(this, _trailAnimationFrameId, requestAnimationFrame(
@@ -1014,7 +1280,7 @@ class SceneRenderProfiler {
1014
1280
  var _a;
1015
1281
  writeSceneLog("SceneRenderProfiler", "Trying to complete profile", __privateGet$2(this, _profileInProgress));
1016
1282
  if (((_a = this.queryController) == null ? void 0 : _a.runningQueriesCount()) === 0 && __privateGet$2(this, _profileInProgress)) {
1017
- writeSceneLog("SceneRenderProfiler", "All queries completed, stopping profile");
1283
+ writeSceneLog("SceneRenderProfiler", "All queries completed, starting tail measurement");
1018
1284
  this.recordProfileTail(performance.now(), __privateGet$2(this, _profileStartTs));
1019
1285
  }
1020
1286
  }
@@ -1037,7 +1303,11 @@ class SceneRenderProfiler {
1037
1303
  cancelAnimationFrame(__privateGet$2(this, _trailAnimationFrameId));
1038
1304
  __privateSet$2(this, _trailAnimationFrameId, null);
1039
1305
  }
1306
+ __privateGet$2(this, _longFrameDetector).stop();
1307
+ writeSceneLog("SceneRenderProfiler", "Stopped long frame detection - profile cancelled");
1040
1308
  __privateSet$2(this, _recordedTrailingSpans, []);
1309
+ __privateSet$2(this, _longFramesCount, 0);
1310
+ __privateSet$2(this, _longFramesTotalTime, 0);
1041
1311
  }
1042
1312
  }
1043
1313
  addCrumb(crumb) {
@@ -1051,10 +1321,13 @@ _profileInProgress = new WeakMap();
1051
1321
  _profileStartTs = new WeakMap();
1052
1322
  _trailAnimationFrameId = new WeakMap();
1053
1323
  _recordedTrailingSpans = new WeakMap();
1324
+ _longFrameDetector = new WeakMap();
1325
+ _longFramesCount = new WeakMap();
1326
+ _longFramesTotalTime = new WeakMap();
1054
1327
  _visibilityChangeHandler = new WeakMap();
1055
1328
  function processRecordedSpans(spans) {
1056
1329
  for (let i = spans.length - 1; i >= 0; i--) {
1057
- if (spans[i] > SPAN_THRESHOLD) {
1330
+ if (spans[i] > DEFAULT_LONG_FRAME_THRESHOLD) {
1058
1331
  return spans.slice(0, i + 1);
1059
1332
  }
1060
1333
  }