jazz-tools 0.20.1 → 0.20.2

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.
Files changed (115) hide show
  1. package/.turbo/turbo-build.log +48 -48
  2. package/CHANGELOG.md +10 -0
  3. package/dist/{chunk-2OPP7KWV.js → chunk-Q5RNSSUM.js} +121 -22
  4. package/dist/chunk-Q5RNSSUM.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/inspector/{chunk-MCTB5ZJC.js → chunk-6JPVMI3V.js} +302 -182
  7. package/dist/inspector/chunk-6JPVMI3V.js.map +1 -0
  8. package/dist/inspector/{custom-element-5YWVZBWA.js → custom-element-PWRX4VCA.js} +1337 -206
  9. package/dist/inspector/custom-element-PWRX4VCA.js.map +1 -0
  10. package/dist/inspector/in-app.d.ts +1 -0
  11. package/dist/inspector/in-app.d.ts.map +1 -1
  12. package/dist/inspector/index.d.ts +1 -0
  13. package/dist/inspector/index.d.ts.map +1 -1
  14. package/dist/inspector/index.js +1044 -17
  15. package/dist/inspector/index.js.map +1 -1
  16. package/dist/inspector/pages/home.d.ts +4 -1
  17. package/dist/inspector/pages/home.d.ts.map +1 -1
  18. package/dist/inspector/pages/performance/PerformancePage.d.ts +7 -0
  19. package/dist/inspector/pages/performance/PerformancePage.d.ts.map +1 -0
  20. package/dist/inspector/pages/performance/SubscriptionDetailPanel.d.ts +8 -0
  21. package/dist/inspector/pages/performance/SubscriptionDetailPanel.d.ts.map +1 -0
  22. package/dist/inspector/pages/performance/SubscriptionRow.d.ts +11 -0
  23. package/dist/inspector/pages/performance/SubscriptionRow.d.ts.map +1 -0
  24. package/dist/inspector/pages/performance/Timeline.d.ts +12 -0
  25. package/dist/inspector/pages/performance/Timeline.d.ts.map +1 -0
  26. package/dist/inspector/pages/performance/helpers.d.ts +5 -0
  27. package/dist/inspector/pages/performance/helpers.d.ts.map +1 -0
  28. package/dist/inspector/pages/performance/index.d.ts +3 -0
  29. package/dist/inspector/pages/performance/index.d.ts.map +1 -0
  30. package/dist/inspector/pages/performance/types.d.ts +13 -0
  31. package/dist/inspector/pages/performance/types.d.ts.map +1 -0
  32. package/dist/inspector/pages/performance/usePerformanceEntries.d.ts +3 -0
  33. package/dist/inspector/pages/performance/usePerformanceEntries.d.ts.map +1 -0
  34. package/dist/inspector/register-custom-element.js +3 -1
  35. package/dist/inspector/register-custom-element.js.map +1 -1
  36. package/dist/inspector/standalone.js +1 -1
  37. package/dist/inspector/tests/pages/performance/PerformancePage.test.d.ts +2 -0
  38. package/dist/inspector/tests/pages/performance/PerformancePage.test.d.ts.map +1 -0
  39. package/dist/inspector/tests/pages/performance/SubscriptionDetailPanel.test.d.ts +2 -0
  40. package/dist/inspector/tests/pages/performance/SubscriptionDetailPanel.test.d.ts.map +1 -0
  41. package/dist/inspector/tests/pages/performance/SubscriptionRow.test.d.ts +2 -0
  42. package/dist/inspector/tests/pages/performance/SubscriptionRow.test.d.ts.map +1 -0
  43. package/dist/inspector/tests/pages/performance/Timeline.test.d.ts +2 -0
  44. package/dist/inspector/tests/pages/performance/Timeline.test.d.ts.map +1 -0
  45. package/dist/inspector/tests/pages/performance/helpers.test.d.ts +2 -0
  46. package/dist/inspector/tests/pages/performance/helpers.test.d.ts.map +1 -0
  47. package/dist/inspector/viewer/delete-local-data.d.ts.map +1 -1
  48. package/dist/inspector/viewer/header.d.ts +4 -2
  49. package/dist/inspector/viewer/header.d.ts.map +1 -1
  50. package/dist/inspector/viewer/page-stack.d.ts +3 -1
  51. package/dist/inspector/viewer/page-stack.d.ts.map +1 -1
  52. package/dist/react-core/hooks.d.ts +2 -2
  53. package/dist/react-core/hooks.d.ts.map +1 -1
  54. package/dist/react-core/index.js +50 -18
  55. package/dist/react-core/index.js.map +1 -1
  56. package/dist/react-core/subscription-provider.d.ts.map +1 -1
  57. package/dist/react-native-core/media/image.d.ts +1 -1
  58. package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
  59. package/dist/svelte/jazz.class.svelte.js +27 -22
  60. package/dist/testing.js +1 -1
  61. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  62. package/dist/tools/exports.d.ts +1 -1
  63. package/dist/tools/exports.d.ts.map +1 -1
  64. package/dist/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts.map +1 -1
  65. package/dist/tools/subscribe/SubscriptionCache.d.ts +2 -2
  66. package/dist/tools/subscribe/SubscriptionCache.d.ts.map +1 -1
  67. package/dist/tools/subscribe/SubscriptionScope.d.ts +19 -12
  68. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  69. package/dist/tools/subscribe/errorReporting.d.ts +6 -0
  70. package/dist/tools/subscribe/errorReporting.d.ts.map +1 -1
  71. package/dist/tools/subscribe/index.d.ts +4 -4
  72. package/dist/tools/subscribe/index.d.ts.map +1 -1
  73. package/dist/tools/subscribe/types.d.ts +48 -3
  74. package/dist/tools/subscribe/types.d.ts.map +1 -1
  75. package/dist/tools/subscribe/utils.d.ts +1 -1
  76. package/dist/tools/subscribe/utils.d.ts.map +1 -1
  77. package/dist/tools/tests/SubscriptionScope.performance.test.d.ts +2 -0
  78. package/dist/tools/tests/SubscriptionScope.performance.test.d.ts.map +1 -0
  79. package/package.json +4 -4
  80. package/src/inspector/in-app.tsx +41 -3
  81. package/src/inspector/index.tsx +5 -1
  82. package/src/inspector/pages/home.tsx +26 -3
  83. package/src/inspector/pages/performance/PerformancePage.tsx +215 -0
  84. package/src/inspector/pages/performance/SubscriptionDetailPanel.tsx +182 -0
  85. package/src/inspector/pages/performance/SubscriptionRow.tsx +242 -0
  86. package/src/inspector/pages/performance/Timeline.tsx +513 -0
  87. package/src/inspector/pages/performance/helpers.ts +70 -0
  88. package/src/inspector/pages/performance/index.ts +2 -0
  89. package/src/inspector/pages/performance/types.ts +12 -0
  90. package/src/inspector/pages/performance/usePerformanceEntries.ts +53 -0
  91. package/src/inspector/register-custom-element.ts +3 -0
  92. package/src/inspector/tests/pages/performance/PerformancePage.test.tsx +83 -0
  93. package/src/inspector/tests/pages/performance/SubscriptionDetailPanel.test.tsx +68 -0
  94. package/src/inspector/tests/pages/performance/SubscriptionRow.test.tsx +93 -0
  95. package/src/inspector/tests/pages/performance/Timeline.test.tsx +57 -0
  96. package/src/inspector/tests/pages/performance/helpers.test.ts +91 -0
  97. package/src/inspector/viewer/delete-local-data.tsx +24 -5
  98. package/src/inspector/viewer/header.tsx +96 -17
  99. package/src/inspector/viewer/page-stack.tsx +22 -18
  100. package/src/react-core/hooks.ts +34 -4
  101. package/src/react-core/subscription-provider.tsx +17 -8
  102. package/src/svelte/jazz.class.svelte.ts +51 -33
  103. package/src/tools/coValues/interfaces.ts +3 -0
  104. package/src/tools/exports.ts +1 -0
  105. package/src/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.ts +13 -0
  106. package/src/tools/subscribe/SubscriptionCache.ts +6 -4
  107. package/src/tools/subscribe/SubscriptionScope.ts +141 -23
  108. package/src/tools/subscribe/errorReporting.ts +1 -1
  109. package/src/tools/subscribe/index.ts +1 -1
  110. package/src/tools/subscribe/types.ts +62 -9
  111. package/src/tools/subscribe/utils.ts +2 -2
  112. package/src/tools/tests/SubscriptionScope.performance.test.ts +149 -0
  113. package/dist/chunk-2OPP7KWV.js.map +0 -1
  114. package/dist/inspector/chunk-MCTB5ZJC.js.map +0 -1
  115. package/dist/inspector/custom-element-5YWVZBWA.js.map +0 -1
@@ -2,18 +2,22 @@
2
2
  import {
3
3
  GlobalStyles,
4
4
  Header,
5
+ HomePage,
5
6
  InMemoryRouterProvider,
6
7
  NodeProvider,
7
- PageStack
8
- } from "./chunk-MCTB5ZJC.js";
8
+ PageStack,
9
+ useRouter
10
+ } from "./chunk-6JPVMI3V.js";
9
11
 
10
12
  // src/inspector/index.tsx
11
- import React, { useEffect as useEffect2, useState as useState2 } from "react";
13
+ import React, { useEffect as useEffect4, useState as useState6 } from "react";
12
14
  import { setup } from "goober";
13
15
  import { useJazzContextValue } from "jazz-tools/react-core";
16
+ import { SubscriptionScope as SubscriptionScope2 } from "jazz-tools";
14
17
 
15
18
  // src/inspector/in-app.tsx
16
- import { styled as styled2 } from "goober";
19
+ import { styled as styled6 } from "goober";
20
+ import { useCallback, useState as useState5 } from "react";
17
21
 
18
22
  // src/inspector/viewer/inspector-button.tsx
19
23
  import { styled } from "goober";
@@ -115,30 +119,1049 @@ function useOpenInspector() {
115
119
  return [open, setOpen];
116
120
  }
117
121
 
122
+ // src/inspector/pages/performance/PerformancePage.tsx
123
+ import { styled as styled5 } from "goober";
124
+ import { useMemo as useMemo2, useState as useState4, useDeferredValue } from "react";
125
+
126
+ // src/inspector/pages/performance/Timeline.tsx
127
+ import { styled as styled2 } from "goober";
128
+ import {
129
+ useEffect as useEffect2,
130
+ useMemo,
131
+ useRef,
132
+ useState as useState2
133
+ } from "react";
134
+
135
+ // src/inspector/pages/performance/helpers.ts
136
+ function formatTime(startTime) {
137
+ const date = new Date(performance.timeOrigin + startTime);
138
+ return date.toLocaleTimeString(void 0, {
139
+ hour: "2-digit",
140
+ minute: "2-digit",
141
+ second: "2-digit",
142
+ fractionalSecondDigits: 3
143
+ });
144
+ }
145
+ function formatDuration(duration) {
146
+ if (duration < 1) {
147
+ return `${(duration * 1e3).toFixed(0)}\u03BCs`;
148
+ }
149
+ if (duration < 1e3) {
150
+ return `${duration.toFixed(2)}ms`;
151
+ }
152
+ return `${(duration / 1e3).toFixed(2)}s`;
153
+ }
154
+ function getCallerLocation(stack) {
155
+ if (!stack) return void 0;
156
+ const lines = stack.split("\n").slice(2, 15);
157
+ const normalizeLine = (line) => line.replace(/https?:\/\/[^/\s)]+/g, "");
158
+ const userFrame = lines.find(
159
+ (line) => !line.includes("node_modules") && !line.includes("useCoValueSubscription") && !line.includes("useCoState") && !line.includes("useAccount") && !line.includes("useSuspenseCoState") && !line.includes("useSuspenseAccount") && !line.includes("jazz-tools") && !line.includes("trackLoadingPerformance")
160
+ );
161
+ if (userFrame) {
162
+ const cleanedFrame = normalizeLine(userFrame).trim();
163
+ const match = cleanedFrame.match(/\(?([^)]+:\d+:\d+)\)?$/);
164
+ if (match) {
165
+ return match[1];
166
+ }
167
+ return cleanedFrame;
168
+ }
169
+ return lines[0] ? normalizeLine(lines[0]).trim() : void 0;
170
+ }
171
+ function getCallerStack(stack) {
172
+ if (!stack) return void 0;
173
+ const lines = stack.split("\n").slice(2, 15);
174
+ return lines.filter(
175
+ (line) => !line.includes("Error:") && !line.includes("renderWithHooks") && !line.includes("react-stack-bottom-frame")
176
+ ).map((line) => line.replace(/https?:\/\/[^/\s)]+/g, "").trim()).reverse().join("\n");
177
+ }
178
+
179
+ // src/inspector/pages/performance/Timeline.tsx
180
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
181
+ var TimelineContainer = styled2("div")`
182
+ position: relative;
183
+ display: flex;
184
+ flex-direction: column;
185
+ background-color: var(--j-foreground);
186
+ border: 1px solid var(--j-border-color);
187
+ border-radius: var(--j-radius-sm);
188
+ overflow: hidden;
189
+ `;
190
+ var TimelineTrack = styled2("div")`
191
+ position: relative;
192
+ height: 48px;
193
+ background-color: var(--j-background);
194
+ cursor: crosshair;
195
+ user-select: none;
196
+ `;
197
+ var TimeMarker = styled2("div")`
198
+ position: absolute;
199
+ font-size: 0.5rem;
200
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
201
+ color: var(--j-text-color);
202
+ padding: 2px 4px;
203
+ white-space: nowrap;
204
+
205
+ @media (prefers-color-scheme: dark) {
206
+ color: var(--j-neutral-500);
207
+ }
208
+
209
+ &::after {
210
+ content: "";
211
+ position: absolute;
212
+ left: 0;
213
+ top: 100%;
214
+ width: 1px;
215
+ height: 32px;
216
+ background-color: var(--j-border-color);
217
+ }
218
+ `;
219
+ var TimelineBars = styled2("div")`
220
+ position: absolute;
221
+ top: 16px;
222
+ left: 0;
223
+ right: 0;
224
+ bottom: 0;
225
+ pointer-events: none;
226
+ `;
227
+ var TimelineBar = styled2("div")`
228
+ position: absolute;
229
+ border-radius: 1px;
230
+ min-width: 2px;
231
+ `;
232
+ var TimelineSelection = styled2("div")`
233
+ position: absolute;
234
+ top: 0;
235
+ bottom: 0;
236
+ background-color: var(--j-primary-color);
237
+ opacity: 0.2;
238
+ cursor: grab;
239
+ pointer-events: auto;
240
+
241
+ &:active {
242
+ cursor: grabbing;
243
+ }
244
+ `;
245
+ var TimelineSelectionHandle = styled2("div")`
246
+ position: absolute;
247
+ top: 0;
248
+ bottom: 0;
249
+ width: 2px;
250
+ background-color: var(--j-primary-color);
251
+ cursor: ew-resize;
252
+ pointer-events: auto;
253
+
254
+ &::after {
255
+ content: "";
256
+ position: absolute;
257
+ top: 50%;
258
+ left: 50%;
259
+ transform: translate(-50%, -50%);
260
+ width: 8px;
261
+ height: 16px;
262
+ background-color: var(--j-primary-color);
263
+ border-radius: 2px;
264
+ }
265
+ `;
266
+ var ClearSelectionButton = styled2("button")`
267
+ position: absolute;
268
+ top: 4px;
269
+ right: 4px;
270
+ display: flex;
271
+ align-items: center;
272
+ gap: 4px;
273
+ padding: 2px 6px;
274
+ font-size: 0.5rem;
275
+ background-color: var(--j-foreground);
276
+ border: 1px solid var(--j-border-color);
277
+ border-radius: var(--j-radius-sm);
278
+ cursor: pointer;
279
+ color: var(--j-neutral-500);
280
+ z-index: 10;
281
+
282
+ &:hover {
283
+ background-color: var(--j-background);
284
+ color: var(--j-text-color);
285
+ }
286
+ `;
287
+ function Timeline({
288
+ entries,
289
+ timeRange,
290
+ selection,
291
+ onSelectionChange
292
+ }) {
293
+ const trackRef = useRef(null);
294
+ const [dragMode, setDragMode] = useState2(null);
295
+ const [dragStartTime, setDragStartTime] = useState2(null);
296
+ const [dragCurrentTime, setDragCurrentTime] = useState2(null);
297
+ const [dragInitialSelection, setDragInitialSelection] = useState2(null);
298
+ const duration = timeRange.max - timeRange.min;
299
+ const timeMarkers = useMemo(() => {
300
+ const markers = [];
301
+ if (duration <= 0) return markers;
302
+ const maxMarkers = 5;
303
+ const minInterval = duration / maxMarkers;
304
+ const magnitude = Math.pow(10, Math.floor(Math.log10(minInterval)));
305
+ const normalized = minInterval / magnitude;
306
+ let niceMultiplier;
307
+ if (normalized <= 1) niceMultiplier = 1;
308
+ else if (normalized <= 2) niceMultiplier = 2;
309
+ else if (normalized <= 5) niceMultiplier = 5;
310
+ else niceMultiplier = 10;
311
+ const interval = niceMultiplier * magnitude;
312
+ const startMarker = Math.ceil(timeRange.min / interval) * interval;
313
+ for (let time = startMarker; time <= timeRange.max; time += interval) {
314
+ const position = (time - timeRange.min) / duration * 100;
315
+ markers.push({
316
+ time,
317
+ label: formatDuration(time),
318
+ position
319
+ });
320
+ }
321
+ return markers;
322
+ }, [timeRange, duration]);
323
+ const getTimeFromPosition = (clientX) => {
324
+ if (!trackRef.current) return 0;
325
+ const rect = trackRef.current.getBoundingClientRect();
326
+ const position = Math.max(
327
+ 0,
328
+ Math.min(1, (clientX - rect.left) / rect.width)
329
+ );
330
+ return timeRange.min + position * duration;
331
+ };
332
+ const handleTrackMouseDown = (e) => {
333
+ const time = getTimeFromPosition(e.clientX);
334
+ setDragMode("creating");
335
+ setDragStartTime(time);
336
+ setDragCurrentTime(time);
337
+ };
338
+ const handleSelectionMouseDown = (e) => {
339
+ e.stopPropagation();
340
+ if (!selection) return;
341
+ const time = getTimeFromPosition(e.clientX);
342
+ setDragMode("moving");
343
+ setDragStartTime(time);
344
+ setDragCurrentTime(time);
345
+ setDragInitialSelection(selection);
346
+ };
347
+ const handleLeftHandleMouseDown = (e) => {
348
+ e.stopPropagation();
349
+ if (!selection) return;
350
+ setDragMode("resizing-left");
351
+ setDragStartTime(selection[0]);
352
+ setDragCurrentTime(selection[0]);
353
+ setDragInitialSelection(selection);
354
+ };
355
+ const handleRightHandleMouseDown = (e) => {
356
+ e.stopPropagation();
357
+ if (!selection) return;
358
+ setDragMode("resizing-right");
359
+ setDragStartTime(selection[1]);
360
+ setDragCurrentTime(selection[1]);
361
+ setDragInitialSelection(selection);
362
+ };
363
+ useEffect2(() => {
364
+ if (!dragMode) return;
365
+ const handleMouseMove = (e) => {
366
+ const time = getTimeFromPosition(e.clientX);
367
+ setDragCurrentTime(time);
368
+ };
369
+ const handleMouseUp = () => {
370
+ if (dragMode === "creating" && dragStartTime !== null && dragCurrentTime !== null) {
371
+ const start = Math.min(dragStartTime, dragCurrentTime);
372
+ const end = Math.max(dragStartTime, dragCurrentTime);
373
+ if ((end - start) / duration > 0.01) {
374
+ onSelectionChange([start, end]);
375
+ }
376
+ } else if (dragMode === "moving" && dragInitialSelection && dragStartTime !== null && dragCurrentTime !== null) {
377
+ const delta = dragCurrentTime - dragStartTime;
378
+ const selectionWidth2 = dragInitialSelection[1] - dragInitialSelection[0];
379
+ let newStart = dragInitialSelection[0] + delta;
380
+ let newEnd = dragInitialSelection[1] + delta;
381
+ if (newStart < timeRange.min) {
382
+ newStart = timeRange.min;
383
+ newEnd = timeRange.min + selectionWidth2;
384
+ }
385
+ if (newEnd > timeRange.max) {
386
+ newEnd = timeRange.max;
387
+ newStart = timeRange.max - selectionWidth2;
388
+ }
389
+ onSelectionChange([newStart, newEnd]);
390
+ } else if (dragMode === "resizing-left" && dragInitialSelection && dragCurrentTime !== null) {
391
+ const newStart = Math.min(
392
+ dragCurrentTime,
393
+ dragInitialSelection[1] - duration * 0.01
394
+ );
395
+ onSelectionChange([
396
+ Math.max(timeRange.min, newStart),
397
+ dragInitialSelection[1]
398
+ ]);
399
+ } else if (dragMode === "resizing-right" && dragInitialSelection && dragCurrentTime !== null) {
400
+ const newEnd = Math.max(
401
+ dragCurrentTime,
402
+ dragInitialSelection[0] + duration * 0.01
403
+ );
404
+ onSelectionChange([
405
+ dragInitialSelection[0],
406
+ Math.min(timeRange.max, newEnd)
407
+ ]);
408
+ }
409
+ setDragMode(null);
410
+ setDragStartTime(null);
411
+ setDragCurrentTime(null);
412
+ setDragInitialSelection(null);
413
+ };
414
+ window.addEventListener("mousemove", handleMouseMove);
415
+ window.addEventListener("mouseup", handleMouseUp);
416
+ return () => {
417
+ window.removeEventListener("mousemove", handleMouseMove);
418
+ window.removeEventListener("mouseup", handleMouseUp);
419
+ };
420
+ }, [
421
+ dragMode,
422
+ dragStartTime,
423
+ dragCurrentTime,
424
+ dragInitialSelection,
425
+ duration,
426
+ timeRange,
427
+ onSelectionChange
428
+ ]);
429
+ const laneAssignments = useMemo(() => {
430
+ const now = performance.now();
431
+ const barHeight = 3;
432
+ const barGap = 1;
433
+ const maxLanes = 8;
434
+ const sortedEntries = [...entries].sort(
435
+ (a, b) => a.startTime - b.startTime
436
+ );
437
+ const laneEndTimes = Array(maxLanes).fill(0);
438
+ const assignments = /* @__PURE__ */ new Map();
439
+ for (const entry of sortedEntries) {
440
+ const entryEnd = entry.endTime ?? now;
441
+ let assignedLane = laneEndTimes.findIndex(
442
+ (endTime) => entry.startTime >= endTime
443
+ );
444
+ if (assignedLane === -1) {
445
+ const earliestEnd = Math.min(...laneEndTimes);
446
+ assignedLane = laneEndTimes.indexOf(earliestEnd);
447
+ }
448
+ laneEndTimes[assignedLane] = entryEnd;
449
+ assignments.set(entry.uuid, assignedLane * (barHeight + barGap));
450
+ }
451
+ return assignments;
452
+ }, [entries]);
453
+ const getBarStyle = (entry) => {
454
+ const now = performance.now();
455
+ const start = entry.startTime;
456
+ const end = entry.endTime ?? now;
457
+ const left = (start - timeRange.min) / duration * 100;
458
+ const width = Math.max(0.5, (end - start) / duration * 100);
459
+ const color = entry.status === "pending" ? "var(--j-warning-color)" : entry.status === "error" ? "var(--j-error-color)" : "var(--j-success-color)";
460
+ const top = laneAssignments.get(entry.uuid) ?? 0;
461
+ return {
462
+ left: `${left}%`,
463
+ width: `${width}%`,
464
+ backgroundColor: color,
465
+ top: `${top}px`,
466
+ height: "3px"
467
+ };
468
+ };
469
+ const currentSelection = useMemo(() => {
470
+ if (dragMode === "creating" && dragStartTime !== null && dragCurrentTime !== null) {
471
+ return [
472
+ Math.min(dragStartTime, dragCurrentTime),
473
+ Math.max(dragStartTime, dragCurrentTime)
474
+ ];
475
+ }
476
+ if (dragMode === "moving" && dragInitialSelection && dragStartTime !== null && dragCurrentTime !== null) {
477
+ const delta = dragCurrentTime - dragStartTime;
478
+ const selectionWidth2 = dragInitialSelection[1] - dragInitialSelection[0];
479
+ let newStart = dragInitialSelection[0] + delta;
480
+ let newEnd = dragInitialSelection[1] + delta;
481
+ if (newStart < timeRange.min) {
482
+ newStart = timeRange.min;
483
+ newEnd = timeRange.min + selectionWidth2;
484
+ }
485
+ if (newEnd > timeRange.max) {
486
+ newEnd = timeRange.max;
487
+ newStart = timeRange.max - selectionWidth2;
488
+ }
489
+ return [newStart, newEnd];
490
+ }
491
+ if (dragMode === "resizing-left" && dragInitialSelection && dragCurrentTime !== null) {
492
+ const newStart = Math.max(
493
+ timeRange.min,
494
+ Math.min(dragCurrentTime, dragInitialSelection[1] - duration * 0.01)
495
+ );
496
+ return [newStart, dragInitialSelection[1]];
497
+ }
498
+ if (dragMode === "resizing-right" && dragInitialSelection && dragCurrentTime !== null) {
499
+ const newEnd = Math.min(
500
+ timeRange.max,
501
+ Math.max(dragCurrentTime, dragInitialSelection[0] + duration * 0.01)
502
+ );
503
+ return [dragInitialSelection[0], newEnd];
504
+ }
505
+ return selection;
506
+ }, [
507
+ dragMode,
508
+ dragStartTime,
509
+ dragCurrentTime,
510
+ dragInitialSelection,
511
+ selection,
512
+ timeRange,
513
+ duration
514
+ ]);
515
+ const selectionLeft = currentSelection ? (currentSelection[0] - timeRange.min) / duration * 100 : 0;
516
+ const selectionWidth = currentSelection ? (currentSelection[1] - currentSelection[0]) / duration * 100 : 0;
517
+ return /* @__PURE__ */ jsxs2(TimelineContainer, { children: [
518
+ /* @__PURE__ */ jsxs2(TimelineTrack, { ref: trackRef, onMouseDown: handleTrackMouseDown, children: [
519
+ timeMarkers.map((marker) => /* @__PURE__ */ jsx2(TimeMarker, { style: { left: `${marker.position}%` }, children: marker.label }, marker.time)),
520
+ /* @__PURE__ */ jsx2(TimelineBars, { children: entries.map((entry) => /* @__PURE__ */ jsx2(TimelineBar, { style: getBarStyle(entry) }, entry.uuid)) }),
521
+ currentSelection && /* @__PURE__ */ jsxs2(Fragment, { children: [
522
+ /* @__PURE__ */ jsx2(
523
+ TimelineSelection,
524
+ {
525
+ style: {
526
+ left: `${selectionLeft}%`,
527
+ width: `${selectionWidth}%`
528
+ },
529
+ onMouseDown: handleSelectionMouseDown
530
+ }
531
+ ),
532
+ !dragMode && /* @__PURE__ */ jsxs2(Fragment, { children: [
533
+ /* @__PURE__ */ jsx2(
534
+ TimelineSelectionHandle,
535
+ {
536
+ style: { left: `${selectionLeft}%` },
537
+ onMouseDown: handleLeftHandleMouseDown
538
+ }
539
+ ),
540
+ /* @__PURE__ */ jsx2(
541
+ TimelineSelectionHandle,
542
+ {
543
+ style: { left: `${selectionLeft + selectionWidth}%` },
544
+ onMouseDown: handleRightHandleMouseDown
545
+ }
546
+ )
547
+ ] })
548
+ ] })
549
+ ] }),
550
+ currentSelection && !dragMode && /* @__PURE__ */ jsx2(
551
+ ClearSelectionButton,
552
+ {
553
+ onClick: (e) => {
554
+ e.stopPropagation();
555
+ onSelectionChange(null);
556
+ },
557
+ children: "Clear selection"
558
+ }
559
+ )
560
+ ] });
561
+ }
562
+
563
+ // src/inspector/pages/performance/SubscriptionRow.tsx
564
+ import { styled as styled3 } from "goober";
565
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
566
+ var RowWrapper = styled3("div")`
567
+ display: grid;
568
+ grid-template-columns: subgrid;
569
+ grid-column: 1 / -1;
570
+ position: relative;
571
+ cursor: pointer;
572
+
573
+ &:hover,
574
+ &:focus {
575
+ background-color: var(--j-foreground);
576
+ outline: none;
577
+ }
578
+
579
+ &:focus-visible {
580
+ outline: 2px solid var(--j-primary-color);
581
+ outline-offset: -2px;
582
+ }
583
+
584
+ &[data-expanded="true"] {
585
+ background-color: var(--j-foreground);
586
+ }
587
+ `;
588
+ var TimeBar = styled3("div")`
589
+ position: absolute;
590
+ bottom: 0;
591
+ height: 4px;
592
+ transition: transform 0.15s ease;
593
+ z-index: 1;
594
+ container-type: inline-size;
595
+
596
+ .row-wrapper:hover &, [data-expanded="true"] & {
597
+ transform: scaleY(4);
598
+
599
+ .time-label {
600
+ opacity: 1;
601
+ }
602
+ }
603
+
604
+ &[data-status="pending"] {
605
+ animation: pulse 1.5s ease-in-out infinite;
606
+ }
607
+
608
+ @keyframes pulse {
609
+ 0%,
610
+ 100% {
611
+ opacity: 1;
612
+ }
613
+ 50% {
614
+ opacity: 0.5;
615
+ }
616
+ }
617
+ `;
618
+ var TimeLabel = styled3("span")`
619
+ position: absolute;
620
+ top: 50%;
621
+ transform: translateY(-50%) scaleY(0.25);
622
+ font-size: 0.5rem;
623
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
624
+ color: white;
625
+ white-space: nowrap;
626
+ opacity: 0;
627
+ transition: opacity 0.15s ease;
628
+ pointer-events: none;
629
+ left: 4px;
630
+ --time-label-overflow-color: black;
631
+
632
+ @media (prefers-color-scheme: dark) {
633
+ --time-label-overflow-color: white;
634
+ }
635
+
636
+ @container (max-width: 50px) {
637
+ color: var(--time-label-overflow-color);
638
+ left: 100%;
639
+ margin-left: 4px;
640
+ }
641
+
642
+ [data-near-edge="true"] & {
643
+ @container (max-width: 50px) {
644
+ left: auto;
645
+ right: 100%;
646
+ margin-left: 0;
647
+ margin-right: 4px;
648
+ }
649
+ }
650
+ `;
651
+ var Cell = styled3("div")`
652
+ padding: 0.5rem;
653
+ padding-bottom: 0.75rem;
654
+ font-size: 0.625rem;
655
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
656
+ border-bottom: 1px solid var(--j-border-color);
657
+ overflow: hidden;
658
+ text-overflow: ellipsis;
659
+ white-space: nowrap;
660
+
661
+ &[data-clickable="true"] {
662
+ cursor: pointer;
663
+ color: var(--j-link-color);
664
+ &:hover {
665
+ text-decoration: underline;
666
+ }
667
+ }
668
+ `;
669
+ var StatusBadge = styled3("span")`
670
+ display: inline-flex;
671
+ align-items: center;
672
+ gap: 0.25rem;
673
+ padding: 0.125rem 0.375rem;
674
+ border-radius: 0.25rem;
675
+ font-size: 0.625rem;
676
+
677
+ &[data-status="pending"] {
678
+ background-color: var(--j-warning-bg);
679
+ color: var(--j-warning-color);
680
+ }
681
+ &[data-status="loaded"] {
682
+ background-color: var(--j-success-bg);
683
+ color: var(--j-success-color);
684
+ }
685
+ &[data-status="error"] {
686
+ background-color: var(--j-error-bg);
687
+ color: var(--j-error-color);
688
+ }
689
+ `;
690
+ var PendingDot = styled3("span")`
691
+ display: inline-block;
692
+ width: 8px;
693
+ height: 8px;
694
+ border-radius: 50%;
695
+ background-color: var(--j-warning-color);
696
+ animation: pendingPulse 1.5s ease-in-out infinite;
697
+
698
+ @keyframes pendingPulse {
699
+ 0%,
700
+ 100% {
701
+ opacity: 1;
702
+ }
703
+ 50% {
704
+ opacity: 0.4;
705
+ }
706
+ }
707
+ `;
708
+ function SubscriptionRow({
709
+ entry,
710
+ isSelected,
711
+ onSelect,
712
+ barLeft,
713
+ barWidth,
714
+ barColor
715
+ }) {
716
+ const handleKeyDown = (e) => {
717
+ if (e.key === "Enter" || e.key === " ") {
718
+ e.preventDefault();
719
+ onSelect();
720
+ }
721
+ };
722
+ return /* @__PURE__ */ jsxs3(
723
+ RowWrapper,
724
+ {
725
+ className: "row-wrapper",
726
+ "data-expanded": isSelected,
727
+ onClick: onSelect,
728
+ onKeyDown: handleKeyDown,
729
+ tabIndex: 0,
730
+ role: "button",
731
+ "aria-label": `View details for ${entry.source} ${entry.id}`,
732
+ children: [
733
+ /* @__PURE__ */ jsx3(Cell, { children: /* @__PURE__ */ jsx3(StatusBadge, { "data-status": entry.status, children: entry.source }) }),
734
+ /* @__PURE__ */ jsx3(Cell, { children: /* @__PURE__ */ jsxs3(
735
+ "div",
736
+ {
737
+ style: {
738
+ display: "flex",
739
+ flexDirection: "column",
740
+ gap: "0.125rem"
741
+ },
742
+ children: [
743
+ /* @__PURE__ */ jsx3("span", { children: entry.id }),
744
+ /* @__PURE__ */ jsx3("span", { style: { color: "var(--j-neutral-500)" }, children: entry.resolve })
745
+ ]
746
+ }
747
+ ) }),
748
+ /* @__PURE__ */ jsx3(Cell, { children: getCallerLocation(entry.callerStack) ?? "-" }),
749
+ /* @__PURE__ */ jsx3(Cell, { children: entry.duration !== void 0 ? entry.duration === 0 ? "\u26A1 cached" : formatDuration(entry.duration) : /* @__PURE__ */ jsx3(PendingDot, {}) }),
750
+ /* @__PURE__ */ jsx3(
751
+ TimeBar,
752
+ {
753
+ className: "time-bar",
754
+ "data-status": entry.status,
755
+ "data-near-edge": parseFloat(barLeft) + parseFloat(barWidth) > 85,
756
+ style: {
757
+ left: barLeft,
758
+ width: barWidth,
759
+ backgroundColor: barColor
760
+ },
761
+ children: /* @__PURE__ */ jsx3(TimeLabel, { className: "time-label", children: entry.duration === 0 ? "\u26A1 cached" : entry.duration ? formatDuration(entry.duration) : "-" })
762
+ }
763
+ )
764
+ ]
765
+ }
766
+ );
767
+ }
768
+
769
+ // src/inspector/pages/performance/SubscriptionDetailPanel.tsx
770
+ import { styled as styled4 } from "goober";
771
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
772
+ var DetailPanel = styled4("div")`
773
+ width: 320px;
774
+ flex-shrink: 0;
775
+ padding: 0.75rem 1rem;
776
+ background-color: var(--j-foreground);
777
+ border: 1px solid var(--j-border-color);
778
+ border-radius: var(--j-radius-sm);
779
+ overflow-y: auto;
780
+ position: relative;
781
+ `;
782
+ var CloseButton = styled4("button")`
783
+ position: absolute;
784
+ top: 0.5rem;
785
+ right: 0.5rem;
786
+ display: flex;
787
+ align-items: center;
788
+ justify-content: center;
789
+ width: 18px;
790
+ height: 18px;
791
+ padding: 0;
792
+ background: none;
793
+ border: none;
794
+ border-radius: var(--j-radius-sm);
795
+ cursor: pointer;
796
+ color: var(--j-neutral-500);
797
+
798
+ &:hover {
799
+ background-color: var(--j-background);
800
+ color: var(--j-text-color);
801
+ }
802
+
803
+ &:focus-visible {
804
+ outline: 2px solid var(--j-primary-color);
805
+ outline-offset: -2px;
806
+ }
807
+ `;
808
+ var DetailsGrid = styled4("div")`
809
+ display: grid;
810
+ grid-template-columns: auto 1fr;
811
+ gap: 0.5rem 1rem;
812
+ font-size: 0.625rem;
813
+ `;
814
+ var DetailLabel = styled4("span")`
815
+ color: var(--j-neutral-500);
816
+ font-weight: 500;
817
+ `;
818
+ var DetailValue = styled4("span")`
819
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
820
+ color: var(--j-text-color);
821
+ `;
822
+ var Pre = styled4("pre")`
823
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
824
+ font-size: 0.625rem;
825
+ color: var(--j-text-color);
826
+ margin: 0;
827
+ white-space: pre-wrap;
828
+ word-break: break-all;
829
+ max-height: 200px;
830
+ overflow-y: auto;
831
+ background-color: var(--j-background);
832
+ padding: 0.5rem;
833
+ border-radius: var(--j-radius-sm);
834
+ `;
835
+ var StatusBadge2 = styled4("span")`
836
+ display: inline-flex;
837
+ align-items: center;
838
+ gap: 0.25rem;
839
+ padding: 0.125rem 0.375rem;
840
+ border-radius: 0.25rem;
841
+ font-size: 0.625rem;
842
+
843
+ &[data-status="pending"] {
844
+ background-color: var(--j-warning-bg);
845
+ color: var(--j-warning-color);
846
+ }
847
+ &[data-status="loaded"] {
848
+ background-color: var(--j-success-bg);
849
+ color: var(--j-success-color);
850
+ }
851
+ &[data-status="error"] {
852
+ background-color: var(--j-error-bg);
853
+ color: var(--j-error-color);
854
+ }
855
+ `;
856
+ function SubscriptionDetailPanel({
857
+ entry,
858
+ onNavigate,
859
+ onClose
860
+ }) {
861
+ return /* @__PURE__ */ jsxs4(DetailPanel, { children: [
862
+ /* @__PURE__ */ jsx4(CloseButton, { onClick: onClose, "aria-label": "Close detail panel", children: /* @__PURE__ */ jsx4(
863
+ "svg",
864
+ {
865
+ width: "10",
866
+ height: "10",
867
+ viewBox: "0 0 14 14",
868
+ fill: "none",
869
+ xmlns: "http://www.w3.org/2000/svg",
870
+ children: /* @__PURE__ */ jsx4(
871
+ "path",
872
+ {
873
+ d: "M1 1L13 13M1 13L13 1",
874
+ stroke: "currentColor",
875
+ strokeWidth: "2",
876
+ strokeLinecap: "round"
877
+ }
878
+ )
879
+ }
880
+ ) }),
881
+ /* @__PURE__ */ jsxs4(DetailsGrid, { children: [
882
+ /* @__PURE__ */ jsx4(DetailLabel, { children: "Source" }),
883
+ /* @__PURE__ */ jsx4(DetailValue, { children: /* @__PURE__ */ jsx4(StatusBadge2, { "data-status": entry.status, children: entry.source }) }),
884
+ /* @__PURE__ */ jsx4(DetailLabel, { children: "CoValue" }),
885
+ /* @__PURE__ */ jsx4(DetailValue, { children: /* @__PURE__ */ jsx4(
886
+ "button",
887
+ {
888
+ title: "Click to navigate to CoValue",
889
+ onClick: () => onNavigate(entry.id),
890
+ style: {
891
+ color: "var(--j-link-color)",
892
+ cursor: "pointer",
893
+ background: "none",
894
+ border: "none",
895
+ padding: 0,
896
+ font: "inherit"
897
+ },
898
+ children: entry.id
899
+ }
900
+ ) }),
901
+ /* @__PURE__ */ jsx4(DetailLabel, { children: "Time" }),
902
+ /* @__PURE__ */ jsxs4(DetailValue, { children: [
903
+ formatTime(entry.startTime),
904
+ " -",
905
+ " ",
906
+ entry.duration !== void 0 ? formatTime(entry.startTime + entry.duration) : "Pending..."
907
+ ] }),
908
+ /* @__PURE__ */ jsx4(DetailLabel, { children: "Duration" }),
909
+ /* @__PURE__ */ jsx4(DetailValue, { children: entry.duration !== void 0 ? formatDuration(entry.duration) : "Pending..." }),
910
+ /* @__PURE__ */ jsx4(DetailLabel, { children: "Resolve Query" }),
911
+ /* @__PURE__ */ jsx4(Pre, { children: JSON.stringify(JSON.parse(entry.resolve), null, 2) }),
912
+ /* @__PURE__ */ jsx4(DetailLabel, { children: "Stack Trace" }),
913
+ /* @__PURE__ */ jsx4(Pre, { children: getCallerStack(entry.callerStack) ?? "No stack trace available" })
914
+ ] })
915
+ ] });
916
+ }
917
+
918
+ // src/inspector/pages/performance/usePerformanceEntries.ts
919
+ import { useState as useState3, useEffect as useEffect3 } from "react";
920
+ function usePerformanceEntries() {
921
+ const [entries, setEntries] = useState3([]);
922
+ useEffect3(() => {
923
+ const entriesByUuid = /* @__PURE__ */ new Map();
924
+ const handlePerformanceEntries = (entries2) => {
925
+ for (const mark of entries2) {
926
+ const detail = mark.detail;
927
+ if (detail?.type !== "jazz-subscription") continue;
928
+ const prevEntry = entriesByUuid.get(detail.uuid);
929
+ if (mark.entryType === "mark" && prevEntry) continue;
930
+ entriesByUuid.set(detail.uuid, {
931
+ uuid: detail.uuid,
932
+ id: detail.id,
933
+ source: detail.source,
934
+ resolve: JSON.stringify(detail.resolve),
935
+ status: detail.status,
936
+ startTime: mark.startTime,
937
+ callerStack: detail.callerStack ?? prevEntry?.callerStack,
938
+ duration: mark.entryType === "mark" ? void 0 : mark.duration,
939
+ endTime: mark.startTime + mark.duration,
940
+ errorType: detail.errorType
941
+ });
942
+ }
943
+ };
944
+ handlePerformanceEntries(performance.getEntriesByType("mark"));
945
+ handlePerformanceEntries(performance.getEntriesByType("measure"));
946
+ setEntries(Array.from(entriesByUuid.values()));
947
+ const observer = new PerformanceObserver((list) => {
948
+ handlePerformanceEntries(list.getEntries());
949
+ setEntries(Array.from(entriesByUuid.values()));
950
+ });
951
+ observer.observe({ entryTypes: ["mark", "measure"] });
952
+ return () => observer.disconnect();
953
+ }, []);
954
+ return entries;
955
+ }
956
+
957
+ // src/inspector/pages/performance/PerformancePage.tsx
958
+ import { SubscriptionScope } from "jazz-tools";
959
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
960
+ var Container = styled5("div")`
961
+ display: flex;
962
+ flex-direction: column;
963
+ gap: 0.5rem;
964
+ padding: 1rem;
965
+ height: 100%;
966
+ min-height: 0;
967
+ `;
968
+ var MainLayout = styled5("div")`
969
+ display: flex;
970
+ flex: 1;
971
+ min-height: 0;
972
+ gap: 1rem;
973
+ `;
974
+ var ListPanel = styled5("div")`
975
+ flex: 1;
976
+ min-width: 0;
977
+ display: flex;
978
+ flex-direction: column;
979
+ `;
980
+ var Grid = styled5("div")`
981
+ display: grid;
982
+ grid-template-columns:
983
+ minmax(100px, 150px)
984
+ minmax(150px, 1fr)
985
+ minmax(100px, 200px)
986
+ 80px;
987
+ grid-template-rows: min-content;
988
+ overflow-y: auto;
989
+ overflow-x: hidden;
990
+ flex: 1;
991
+ min-height: 0;
992
+ position: relative;
993
+ `;
994
+ var HeaderCell = styled5("div")`
995
+ padding: 0.5rem;
996
+ font-size: 0.625rem;
997
+ font-weight: 600;
998
+ color: var(--j-neutral-500);
999
+ border-bottom: 1px solid var(--j-border-color);
1000
+ text-transform: uppercase;
1001
+ letter-spacing: 0.05em;
1002
+ `;
1003
+ var EmptyState = styled5("div")`
1004
+ text-align: center;
1005
+ padding: 2rem;
1006
+ color: var(--j-neutral-500);
1007
+ font-size: 0.875rem;
1008
+ `;
1009
+ function PerformancePage({ onNavigate, style }) {
1010
+ const entries = usePerformanceEntries();
1011
+ const [selectedRow, setSelectedRow] = useState4(null);
1012
+ const [timeSelection, setTimeSelection] = useState4(
1013
+ null
1014
+ );
1015
+ const deferredSelection = useDeferredValue(timeSelection);
1016
+ const { setPage } = useRouter();
1017
+ const selectRow = (uuid) => {
1018
+ setSelectedRow((prev) => prev === uuid ? null : uuid);
1019
+ };
1020
+ const sortedEntries = useMemo2(() => {
1021
+ return [...entries].sort((a, b) => a.startTime - b.startTime);
1022
+ }, [entries]);
1023
+ const overallTimeRange = useMemo2(() => {
1024
+ if (entries.length === 0) return null;
1025
+ const now = performance.now();
1026
+ return {
1027
+ min: Math.min(...entries.map((e) => e.startTime)),
1028
+ max: Math.max(...entries.map((e) => e.endTime ?? now))
1029
+ };
1030
+ }, [entries]);
1031
+ const filteredEntries = useMemo2(() => {
1032
+ if (!deferredSelection) return sortedEntries;
1033
+ const [startTime, endTime] = deferredSelection;
1034
+ const now = performance.now();
1035
+ return sortedEntries.filter((entry) => {
1036
+ const entryEnd = entry.endTime ?? now;
1037
+ return entry.startTime <= endTime && entryEnd >= startTime;
1038
+ });
1039
+ }, [sortedEntries, deferredSelection]);
1040
+ const displayRange = deferredSelection ? { min: deferredSelection[0], max: deferredSelection[1] } : overallTimeRange;
1041
+ const getBarProps = (entry) => {
1042
+ const range = (displayRange?.max ?? 1) - (displayRange?.min ?? 0) || 1;
1043
+ const now = performance.now();
1044
+ const clampedStart = Math.max(entry.startTime, displayRange?.min ?? 0);
1045
+ const clampedEnd = Math.min(entry.endTime ?? now, displayRange?.max ?? now);
1046
+ const left = Math.max(
1047
+ 0,
1048
+ (clampedStart - (displayRange?.min ?? 0)) / range * 100
1049
+ );
1050
+ const width = Math.max(0.5, (clampedEnd - clampedStart) / range * 100);
1051
+ const color = entry.status === "pending" ? "var(--j-warning-color)" : entry.status === "error" ? "var(--j-error-color)" : "var(--j-success-color)";
1052
+ return {
1053
+ barLeft: `${left}%`,
1054
+ barWidth: `${width}%`,
1055
+ barColor: color
1056
+ };
1057
+ };
1058
+ const handleNavigateToCoValue = (id) => {
1059
+ setPage(id);
1060
+ onNavigate();
1061
+ };
1062
+ if (!SubscriptionScope.isProfilingEnabled) {
1063
+ return /* @__PURE__ */ jsx5(Container, { style, children: /* @__PURE__ */ jsx5(EmptyState, { children: "Profiling is not enabled in production builds." }) });
1064
+ }
1065
+ if (entries.length === 0) {
1066
+ return /* @__PURE__ */ jsx5(Container, { style, children: /* @__PURE__ */ jsx5(EmptyState, { children: "No subscriptions recorded yet. Interact with your app to see subscription performance data." }) });
1067
+ }
1068
+ const selectedEntry = selectedRow ? filteredEntries.find((e) => e.uuid === selectedRow) : null;
1069
+ return /* @__PURE__ */ jsxs5(Container, { style, children: [
1070
+ overallTimeRange && /* @__PURE__ */ jsx5(
1071
+ Timeline,
1072
+ {
1073
+ entries: sortedEntries,
1074
+ timeRange: overallTimeRange,
1075
+ selection: timeSelection,
1076
+ onSelectionChange: setTimeSelection
1077
+ }
1078
+ ),
1079
+ /* @__PURE__ */ jsxs5(MainLayout, { children: [
1080
+ /* @__PURE__ */ jsx5(ListPanel, { children: /* @__PURE__ */ jsxs5(Grid, { children: [
1081
+ /* @__PURE__ */ jsx5(HeaderCell, { children: "Source" }),
1082
+ /* @__PURE__ */ jsx5(HeaderCell, { children: "CoValue" }),
1083
+ /* @__PURE__ */ jsx5(HeaderCell, { children: "Caller" }),
1084
+ /* @__PURE__ */ jsx5(HeaderCell, { children: "Duration" }),
1085
+ filteredEntries.map((entry) => /* @__PURE__ */ jsx5(
1086
+ SubscriptionRow,
1087
+ {
1088
+ entry,
1089
+ isSelected: selectedRow === entry.uuid,
1090
+ onSelect: () => selectRow(entry.uuid),
1091
+ ...getBarProps(entry)
1092
+ },
1093
+ entry.uuid
1094
+ ))
1095
+ ] }) }),
1096
+ selectedEntry && /* @__PURE__ */ jsx5(
1097
+ SubscriptionDetailPanel,
1098
+ {
1099
+ entry: selectedEntry,
1100
+ onNavigate: handleNavigateToCoValue,
1101
+ onClose: () => setSelectedRow(null)
1102
+ }
1103
+ )
1104
+ ] })
1105
+ ] });
1106
+ }
1107
+
118
1108
  // src/inspector/in-app.tsx
119
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1109
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1110
+ var STORAGE_KEY2 = "jazz-inspector-tab";
1111
+ function getStoredTab() {
1112
+ try {
1113
+ const stored = localStorage.getItem(STORAGE_KEY2);
1114
+ if (stored === "inspector" || stored === "performance") {
1115
+ return stored;
1116
+ }
1117
+ } catch {
1118
+ }
1119
+ return "inspector";
1120
+ }
120
1121
  function InspectorInApp({
121
1122
  position = "right",
122
1123
  localNode,
123
1124
  accountId
124
1125
  }) {
125
1126
  const [open, setOpen] = useOpenInspector();
1127
+ const [activeTab, setActiveTabState] = useState5(getStoredTab);
1128
+ const setActiveTab = useCallback((tab) => {
1129
+ setActiveTabState(tab);
1130
+ try {
1131
+ localStorage.setItem(STORAGE_KEY2, tab);
1132
+ } catch {
1133
+ }
1134
+ }, []);
126
1135
  if (!open) {
127
- return /* @__PURE__ */ jsx2(InspectorButton, { position, onClick: () => setOpen(true) });
1136
+ return /* @__PURE__ */ jsx6(InspectorButton, { position, onClick: () => setOpen(true) });
128
1137
  }
129
- return /* @__PURE__ */ jsx2(NodeProvider, { localNode: localNode ?? null, accountID: accountId ?? null, children: /* @__PURE__ */ jsx2(InMemoryRouterProvider, { children: /* @__PURE__ */ jsxs2(InspectorContainer, { as: GlobalStyles, style: { zIndex: 999 }, children: [
130
- /* @__PURE__ */ jsx2(
1138
+ return /* @__PURE__ */ jsx6(NodeProvider, { localNode: localNode ?? null, accountID: accountId ?? null, children: /* @__PURE__ */ jsx6(InMemoryRouterProvider, { children: /* @__PURE__ */ jsxs6(InspectorContainer, { as: GlobalStyles, style: { zIndex: 999 }, children: [
1139
+ /* @__PURE__ */ jsx6(
131
1140
  Header,
132
1141
  {
133
- showDeleteLocalData: true,
134
1142
  showClose: true,
135
- onClose: () => setOpen(false)
1143
+ onClose: () => setOpen(false),
1144
+ activeTab,
1145
+ onTabChange: setActiveTab
1146
+ }
1147
+ ),
1148
+ /* @__PURE__ */ jsx6(
1149
+ PageStack,
1150
+ {
1151
+ style: { display: activeTab === "inspector" ? "flex" : "none" },
1152
+ homePage: /* @__PURE__ */ jsx6(HomePage, { showDeleteLocalData: true })
136
1153
  }
137
1154
  ),
138
- /* @__PURE__ */ jsx2(PageStack, {})
1155
+ /* @__PURE__ */ jsx6(
1156
+ PerformancePage,
1157
+ {
1158
+ style: { display: activeTab === "performance" ? "flex" : "none" },
1159
+ onNavigate: () => setActiveTab("inspector")
1160
+ }
1161
+ )
139
1162
  ] }) }) });
140
1163
  }
141
- var InspectorContainer = styled2("div")`
1164
+ var InspectorContainer = styled6("div")`
142
1165
  position: fixed;
143
1166
  height: 50vh;
144
1167
  max-height: 800px;
@@ -157,19 +1180,22 @@ var InspectorContainer = styled2("div")`
157
1180
  `;
158
1181
 
159
1182
  // src/inspector/index.tsx
160
- import { jsx as jsx3 } from "react/jsx-runtime";
1183
+ import { jsx as jsx7 } from "react/jsx-runtime";
1184
+ function enableProfiling() {
1185
+ SubscriptionScope2.enableProfiling();
1186
+ }
161
1187
  function JazzInspector({ position = "right" }) {
162
1188
  const context = useJazzContextValue();
163
1189
  const localNode = context.node;
164
1190
  const me = "me" in context ? context.me : void 0;
165
- const [isCSR, setIsCSR] = useState2(false);
166
- useEffect2(() => {
1191
+ const [isCSR, setIsCSR] = useState6(false);
1192
+ useEffect4(() => {
167
1193
  setIsCSR(true);
168
1194
  }, []);
169
1195
  if (!isCSR) {
170
1196
  return null;
171
1197
  }
172
- return /* @__PURE__ */ jsx3(
1198
+ return /* @__PURE__ */ jsx7(
173
1199
  InspectorInApp,
174
1200
  {
175
1201
  position,
@@ -180,6 +1206,7 @@ function JazzInspector({ position = "right" }) {
180
1206
  }
181
1207
  setup(React.createElement);
182
1208
  export {
183
- JazzInspector
1209
+ JazzInspector,
1210
+ enableProfiling
184
1211
  };
185
1212
  //# sourceMappingURL=index.js.map