@oh-my-pi/omp-stats 18.7.0 → 18.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/client/index.css +1 -1
  3. package/dist/client/index.js +96 -96
  4. package/dist/types/aggregator.d.ts +3 -0
  5. package/dist/types/client/api.d.ts +5 -1
  6. package/dist/types/client/data/live.d.ts +16 -11
  7. package/dist/types/client/data/query-store.d.ts +39 -0
  8. package/dist/types/client/data/query.d.ts +10 -5
  9. package/dist/types/client/traces/Minimap.d.ts +2 -0
  10. package/dist/types/client/traces/TimelineCanvas.d.ts +29 -8
  11. package/dist/types/client/traces/TranscriptList.d.ts +3 -1
  12. package/dist/types/db.d.ts +14 -2
  13. package/dist/types/embedded-client.d.ts +5 -0
  14. package/dist/types/live.d.ts +5 -3
  15. package/dist/types/rollup.d.ts +9 -1
  16. package/dist/types/trace.d.ts +18 -6
  17. package/package.json +4 -4
  18. package/src/aggregator.ts +26 -9
  19. package/src/client/api.ts +40 -5
  20. package/src/client/app/LiveChip.tsx +2 -2
  21. package/src/client/app/Shell.tsx +2 -2
  22. package/src/client/data/live.tsx +85 -45
  23. package/src/client/data/query-store.ts +108 -0
  24. package/src/client/data/query.ts +47 -56
  25. package/src/client/routes/CostsRoute.tsx +4 -2
  26. package/src/client/routes/ErrorsRoute.tsx +3 -1
  27. package/src/client/routes/FrustrationRoute.tsx +1 -1
  28. package/src/client/routes/GainRoute.tsx +3 -1
  29. package/src/client/routes/ModelsRoute.tsx +9 -2
  30. package/src/client/routes/OverviewRoute.tsx +2 -2
  31. package/src/client/routes/ProjectsRoute.tsx +1 -1
  32. package/src/client/routes/ProvidersRoute.tsx +5 -3
  33. package/src/client/routes/RequestsRoute.tsx +1 -1
  34. package/src/client/routes/ToolsRoute.tsx +1 -1
  35. package/src/client/routes/TracesRoute.tsx +5 -4
  36. package/src/client/traces/Minimap.tsx +63 -26
  37. package/src/client/traces/TimelineCanvas.tsx +224 -105
  38. package/src/client/traces/TraceView.tsx +29 -10
  39. package/src/client/traces/TranscriptList.tsx +17 -9
  40. package/src/client/traces/traces.css +26 -0
  41. package/src/client/ui/RequestDrawer.tsx +3 -1
  42. package/src/db.ts +106 -64
  43. package/src/embedded-client.ts +9 -0
  44. package/src/live.ts +36 -11
  45. package/src/parser.ts +1 -8
  46. package/src/rollup.ts +41 -20
  47. package/src/server.ts +38 -24
  48. package/src/trace.ts +54 -40
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Timeline overview strip: per-category density over the full virtual domain
3
3
  * plus a draggable/resizable viewport brush, mirroring DevTools' overview.
4
+ * The density layer is painted once per trace/scale/size/theme into an
5
+ * offscreen canvas; pan and zoom only blit it and redraw the brush.
4
6
  */
5
7
 
6
- import { useCallback, useEffect, useRef, useState } from "react";
8
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
9
  import type { TraceSpanKind, TraceTrack } from "../types";
8
10
  import type { TimelineViewport } from "./TimelineCanvas";
9
11
  import type { TraceScale } from "./time-scale";
10
- import { useTraceTheme } from "./trace-colors";
12
+ import { type TraceTheme, useTraceTheme } from "./trace-colors";
11
13
 
12
14
  export interface MinimapProps {
13
15
  tracks: TraceTrack[];
@@ -23,6 +25,59 @@ const MIN_WINDOW_U = 10;
23
25
 
24
26
  type DragMode = "move" | "left" | "right" | "create";
25
27
 
28
+ /** Paint the density strips (bucketed span coverage per pixel per category) at device resolution. */
29
+ function paintDensity(
30
+ tracks: TraceTrack[],
31
+ scale: TraceScale,
32
+ width: number,
33
+ colors: TraceTheme,
34
+ ): OffscreenCanvas | HTMLCanvasElement {
35
+ const dpr = devicePixelRatio;
36
+ const pixelWidth = Math.floor(width * dpr);
37
+ const pixelHeight = Math.floor(HEIGHT * dpr);
38
+ let layer: OffscreenCanvas | HTMLCanvasElement;
39
+ let ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null;
40
+ if (typeof OffscreenCanvas === "undefined") {
41
+ const canvas = document.createElement("canvas");
42
+ canvas.width = pixelWidth;
43
+ canvas.height = pixelHeight;
44
+ layer = canvas;
45
+ ctx = canvas.getContext("2d");
46
+ } else {
47
+ const canvas = new OffscreenCanvas(pixelWidth, pixelHeight);
48
+ layer = canvas;
49
+ ctx = canvas.getContext("2d");
50
+ }
51
+ if (!ctx) return layer;
52
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
53
+
54
+ const [d0, d1] = scale.domain;
55
+ const dSpan = Math.max(d1 - d0, 1e-9);
56
+ const buckets = STRIP_KINDS.map(() => new Float32Array(width));
57
+ for (const track of tracks) {
58
+ for (const span of track.spans) {
59
+ const k = STRIP_KINDS.indexOf(span.kind);
60
+ if (k === -1) continue;
61
+ const strip = buckets[k];
62
+ const x0 = Math.max(0, Math.floor(((scale.toU(span.start) - d0) / dSpan) * width));
63
+ const x1 = Math.min(width - 1, Math.ceil(((scale.toU(span.end) - d0) / dSpan) * width));
64
+ for (let x = x0; x <= x1; x++) strip[x] = Math.min(1, strip[x] + 0.34);
65
+ }
66
+ }
67
+ const stripH = HEIGHT / STRIP_KINDS.length;
68
+ for (let k = 0; k < STRIP_KINDS.length; k++) {
69
+ const strip = buckets[k];
70
+ ctx.fillStyle = colors.category[STRIP_KINDS[k]];
71
+ for (let x = 0; x < width; x++) {
72
+ if (strip[x] <= 0) continue;
73
+ ctx.globalAlpha = 0.25 + strip[x] * 0.75;
74
+ ctx.fillRect(x, k * stripH + 1, 1, stripH - 2);
75
+ }
76
+ ctx.globalAlpha = 1;
77
+ }
78
+ return layer;
79
+ }
80
+
26
81
  export function Minimap({ tracks, scale, viewport, onViewportChange }: MinimapProps) {
27
82
  const colors = useTraceTheme();
28
83
  const canvasRef = useRef<HTMLCanvasElement>(null);
@@ -44,36 +99,18 @@ export function Minimap({ tracks, scale, viewport, onViewportChange }: MinimapPr
44
99
  const toX = useCallback((u: number) => ((u - d0) / dSpan) * width, [d0, dSpan, width]);
45
100
  const toU = useCallback((x: number) => d0 + (x / Math.max(width, 1)) * dSpan, [d0, dSpan, width]);
46
101
 
102
+ const density = useMemo(() => paintDensity(tracks, scale, width, colors), [tracks, scale, width, colors]);
103
+
47
104
  useEffect(() => {
48
105
  const canvas = canvasRef.current;
49
106
  if (!canvas) return;
50
107
  const ctx = canvas.getContext("2d");
51
108
  if (!ctx) return;
52
109
  const dpr = devicePixelRatio;
110
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
111
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
112
+ ctx.drawImage(density, 0, 0);
53
113
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
54
- ctx.clearRect(0, 0, width, HEIGHT);
55
-
56
- // Density strips: bucket span coverage per pixel per category.
57
- const stripH = HEIGHT / STRIP_KINDS.length;
58
- for (let k = 0; k < STRIP_KINDS.length; k++) {
59
- const kind = STRIP_KINDS[k];
60
- const buckets = new Float32Array(width);
61
- for (const track of tracks) {
62
- for (const span of track.spans) {
63
- if (span.kind !== kind) continue;
64
- const x0 = Math.max(0, Math.floor(toX(scale.toU(span.start))));
65
- const x1 = Math.min(width - 1, Math.ceil(toX(scale.toU(span.end))));
66
- for (let x = x0; x <= x1; x++) buckets[x] = Math.min(1, buckets[x] + 0.34);
67
- }
68
- }
69
- ctx.fillStyle = colors.category[kind];
70
- for (let x = 0; x < width; x++) {
71
- if (buckets[x] <= 0) continue;
72
- ctx.globalAlpha = 0.25 + buckets[x] * 0.75;
73
- ctx.fillRect(x, k * stripH + 1, 1, stripH - 2);
74
- }
75
- ctx.globalAlpha = 1;
76
- }
77
114
 
78
115
  // Viewport brush.
79
116
  const vx0 = toX(viewport.u0);
@@ -85,7 +122,7 @@ export function Minimap({ tracks, scale, viewport, onViewportChange }: MinimapPr
85
122
  ctx.fillStyle = colors.selection;
86
123
  ctx.fillRect(vx0, 0, 2, HEIGHT);
87
124
  ctx.fillRect(vx1 - 2, 0, 2, HEIGHT);
88
- }, [tracks, scale, viewport, colors, width, toX]);
125
+ }, [density, viewport, colors, toX]);
89
126
 
90
127
  const handlePointerDown = (event: React.PointerEvent<HTMLCanvasElement>) => {
91
128
  const rect = event.currentTarget.getBoundingClientRect();