@datalayer/core 1.0.2 → 1.0.11

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 (145) hide show
  1. package/README.md +1 -1
  2. package/lib/api/constants.d.ts +6 -0
  3. package/lib/api/constants.js +6 -0
  4. package/lib/api/index.d.ts +1 -0
  5. package/lib/api/index.js +1 -0
  6. package/lib/api/otel/index.d.ts +12 -0
  7. package/lib/api/otel/index.js +16 -0
  8. package/lib/api/otel/logs.d.ts +19 -0
  9. package/lib/api/otel/logs.js +43 -0
  10. package/lib/api/otel/metrics.d.ts +31 -0
  11. package/lib/api/otel/metrics.js +65 -0
  12. package/lib/api/otel/query.d.ts +16 -0
  13. package/lib/api/otel/query.js +37 -0
  14. package/lib/api/otel/services.d.ts +39 -0
  15. package/lib/api/otel/services.js +81 -0
  16. package/lib/api/otel/traces.d.ts +24 -0
  17. package/lib/api/otel/traces.js +53 -0
  18. package/lib/api/otel/types.d.ts +112 -0
  19. package/lib/api/otel/types.js +5 -0
  20. package/lib/api/runtimes/checkpoints.d.ts +122 -0
  21. package/lib/api/runtimes/checkpoints.js +118 -0
  22. package/lib/api/runtimes/index.d.ts +1 -0
  23. package/lib/api/runtimes/index.js +1 -0
  24. package/lib/api/runtimes/runtimes.d.ts +84 -0
  25. package/lib/api/runtimes/runtimes.js +50 -0
  26. package/lib/components/auth/Login.js +1 -1
  27. package/lib/components/display/BusyDots.d.ts +9 -0
  28. package/lib/components/display/BusyDots.js +31 -0
  29. package/lib/components/display/LiveRelativeTime.d.ts +10 -0
  30. package/lib/components/display/LiveRelativeTime.js +21 -0
  31. package/lib/components/display/index.d.ts +2 -0
  32. package/lib/components/display/index.js +2 -0
  33. package/lib/components/flashes/FlashSurveys.js +1 -1
  34. package/lib/components/index.d.ts +1 -0
  35. package/lib/components/index.js +1 -0
  36. package/lib/components/navbar/SubdomainNavBar.js +1 -1
  37. package/lib/components/progress/ConsumptionBar.js +6 -7
  38. package/lib/components/progress/CreditsIndicator.js +2 -2
  39. package/lib/components/progress/consumption.d.ts +12 -0
  40. package/lib/components/progress/consumption.js +31 -0
  41. package/lib/components/progress/index.d.ts +1 -0
  42. package/lib/components/progress/index.js +1 -0
  43. package/lib/components/sparklines/Sparklines.d.ts +16 -0
  44. package/lib/components/sparklines/Sparklines.js +65 -0
  45. package/lib/components/sparklines/SparklinesLine.d.ts +8 -0
  46. package/lib/components/sparklines/SparklinesLine.js +37 -0
  47. package/lib/components/sparklines/dataProcessing.d.ts +25 -0
  48. package/lib/components/sparklines/dataProcessing.js +35 -0
  49. package/lib/components/sparklines/index.d.ts +4 -0
  50. package/lib/components/sparklines/index.js +7 -0
  51. package/lib/components/sparklines/types.d.ts +36 -0
  52. package/lib/components/sparklines/types.js +5 -0
  53. package/lib/components/storage/ContentsBrowser.js +17 -1
  54. package/lib/components/subnav/SubNav.js +1 -1
  55. package/lib/config/Configuration.d.ts +4 -0
  56. package/lib/hooks/useCache.d.ts +6 -63
  57. package/lib/hooks/useCache.js +35 -205
  58. package/lib/hooks/useProjects.d.ts +1 -1
  59. package/lib/index.d.ts +2 -0
  60. package/lib/index.js +4 -0
  61. package/lib/models/ItemDTO.js +1 -1
  62. package/lib/models/RolesPlatform.js +2 -2
  63. package/lib/models/User.d.ts +2 -0
  64. package/lib/models/User.js +4 -1
  65. package/lib/otel/client/OtelClient.d.ts +93 -0
  66. package/lib/otel/client/OtelClient.js +232 -0
  67. package/lib/otel/client/index.d.ts +2 -0
  68. package/lib/otel/client/index.js +5 -0
  69. package/lib/otel/hooks/index.d.ts +186 -0
  70. package/lib/otel/hooks/index.js +532 -0
  71. package/lib/otel/index.d.ts +34 -0
  72. package/lib/otel/index.js +23 -0
  73. package/lib/otel/types.d.ts +190 -0
  74. package/lib/otel/types.js +5 -0
  75. package/lib/otel/utils.d.ts +33 -0
  76. package/lib/otel/utils.js +181 -0
  77. package/lib/otel/views/OtelLive.d.ts +12 -0
  78. package/lib/otel/views/OtelLive.js +372 -0
  79. package/lib/otel/views/OtelLogsList.d.ts +11 -0
  80. package/lib/otel/views/OtelLogsList.js +137 -0
  81. package/lib/otel/views/OtelMetricsChart.d.ts +22 -0
  82. package/lib/otel/views/OtelMetricsChart.js +300 -0
  83. package/lib/otel/views/OtelMetricsList.d.ts +15 -0
  84. package/lib/otel/views/OtelMetricsList.js +213 -0
  85. package/lib/otel/views/OtelSearchBar.d.ts +11 -0
  86. package/lib/otel/views/OtelSearchBar.js +22 -0
  87. package/lib/otel/views/OtelSpanDetail.d.ts +11 -0
  88. package/lib/otel/views/OtelSpanDetail.js +172 -0
  89. package/lib/otel/views/OtelSpanTree.d.ts +11 -0
  90. package/lib/otel/views/OtelSpanTree.js +176 -0
  91. package/lib/otel/views/OtelSqlView.d.ts +16 -0
  92. package/lib/otel/views/OtelSqlView.js +239 -0
  93. package/lib/otel/views/OtelSystemView.d.ts +15 -0
  94. package/lib/otel/views/OtelSystemView.js +75 -0
  95. package/lib/otel/views/OtelTimeline.d.ts +11 -0
  96. package/lib/otel/views/OtelTimeline.js +101 -0
  97. package/lib/otel/views/OtelTimelineRangeSlider.d.ts +16 -0
  98. package/lib/otel/views/OtelTimelineRangeSlider.js +338 -0
  99. package/lib/otel/views/OtelTracesList.d.ts +13 -0
  100. package/lib/otel/views/OtelTracesList.js +199 -0
  101. package/lib/otel/views/index.d.ts +20 -0
  102. package/lib/otel/views/index.js +21 -0
  103. package/lib/state/storage/IAMStorage.d.ts +2 -1
  104. package/lib/state/substates/CoreState.js +7 -6
  105. package/lib/utils/Date.d.ts +6 -0
  106. package/lib/utils/Date.js +37 -0
  107. package/lib/utils/Jwt.d.ts +42 -0
  108. package/lib/utils/Jwt.js +44 -0
  109. package/lib/utils/index.d.ts +1 -0
  110. package/lib/utils/index.js +1 -0
  111. package/lib/views/iam/SignInSimple.d.ts +43 -0
  112. package/lib/views/iam/SignInSimple.js +113 -0
  113. package/lib/views/iam/index.d.ts +2 -0
  114. package/lib/views/iam/index.js +5 -0
  115. package/lib/views/iam-tokens/IAMTokenEdit.d.ts +5 -1
  116. package/lib/views/iam-tokens/IAMTokenEdit.js +54 -5
  117. package/lib/views/iam-tokens/IAMTokenNew.js +2 -2
  118. package/lib/views/iam-tokens/IAMTokens.d.ts +4 -2
  119. package/lib/views/iam-tokens/IAMTokens.js +68 -36
  120. package/lib/views/iam-tokens/Tokens.js +63 -31
  121. package/lib/views/index.d.ts +3 -1
  122. package/lib/views/index.js +3 -1
  123. package/lib/views/otel/DashboardView.d.ts +16 -0
  124. package/lib/views/otel/DashboardView.js +4 -0
  125. package/lib/views/otel/LogsView.d.ts +12 -0
  126. package/lib/views/otel/LogsView.js +4 -0
  127. package/lib/views/otel/MetricsView.d.ts +12 -0
  128. package/lib/views/otel/MetricsView.js +4 -0
  129. package/lib/views/otel/OtelHeader.d.ts +33 -0
  130. package/lib/views/otel/OtelHeader.js +105 -0
  131. package/lib/views/otel/SqlView.d.ts +9 -0
  132. package/lib/views/otel/SqlView.js +4 -0
  133. package/lib/views/otel/SystemView.d.ts +9 -0
  134. package/lib/views/otel/SystemView.js +4 -0
  135. package/lib/views/otel/TracesView.d.ts +12 -0
  136. package/lib/views/otel/TracesView.js +4 -0
  137. package/lib/views/otel/index.d.ts +16 -0
  138. package/lib/views/otel/index.js +12 -0
  139. package/lib/views/otel/simpleAuthStore.d.ts +21 -0
  140. package/lib/views/otel/simpleAuthStore.js +22 -0
  141. package/lib/views/profile/UserBadge.d.ts +20 -0
  142. package/lib/views/profile/UserBadge.js +101 -0
  143. package/lib/views/profile/index.d.ts +2 -0
  144. package/lib/views/profile/index.js +5 -0
  145. package/package.json +3 -4
@@ -0,0 +1,300 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as echarts from 'echarts';
3
+ import ReactECharts from 'echarts-for-react';
4
+ import { Box, Text, Label } from '@primer/react';
5
+ // ── Gradient palette ────────────────────────────────────────────────
6
+ const SERIES_COLORS = [
7
+ {
8
+ line: '#5470C6',
9
+ gradientStart: 'rgba(84,112,198,0.7)',
10
+ gradientEnd: 'rgba(84,112,198,0.02)',
11
+ },
12
+ {
13
+ line: '#91CC75',
14
+ gradientStart: 'rgba(145,204,117,0.7)',
15
+ gradientEnd: 'rgba(145,204,117,0.02)',
16
+ },
17
+ {
18
+ line: '#EE6666',
19
+ gradientStart: 'rgba(238,102,102,0.7)',
20
+ gradientEnd: 'rgba(238,102,102,0.02)',
21
+ },
22
+ {
23
+ line: '#FAC858',
24
+ gradientStart: 'rgba(250,200,88,0.7)',
25
+ gradientEnd: 'rgba(250,200,88,0.02)',
26
+ },
27
+ {
28
+ line: '#73C0DE',
29
+ gradientStart: 'rgba(115,192,222,0.7)',
30
+ gradientEnd: 'rgba(115,192,222,0.02)',
31
+ },
32
+ {
33
+ line: '#FC8452',
34
+ gradientStart: 'rgba(252,132,82,0.7)',
35
+ gradientEnd: 'rgba(252,132,82,0.02)',
36
+ },
37
+ {
38
+ line: '#9A60B4',
39
+ gradientStart: 'rgba(154,96,180,0.7)',
40
+ gradientEnd: 'rgba(154,96,180,0.02)',
41
+ },
42
+ {
43
+ line: '#EA7CCC',
44
+ gradientStart: 'rgba(234,124,204,0.7)',
45
+ gradientEnd: 'rgba(234,124,204,0.02)',
46
+ },
47
+ ];
48
+ // ── Helpers ─────────────────────────────────────────────────────────
49
+ /** Group metrics by metric_type, then by metric_name within each type. */
50
+ function groupByType(metrics) {
51
+ const byType = new Map();
52
+ for (const m of metrics) {
53
+ const mtype = m.metric_type || 'other';
54
+ if (!byType.has(mtype))
55
+ byType.set(mtype, new Map());
56
+ const nameMap = byType.get(mtype);
57
+ const key = m.metric_name || '(unnamed)';
58
+ if (!nameMap.has(key))
59
+ nameMap.set(key, []);
60
+ nameMap.get(key).push({
61
+ ts: new Date(m.timestamp).getTime(),
62
+ value: m.value,
63
+ });
64
+ }
65
+ // Sort points chronologically within each name group.
66
+ for (const nameMap of byType.values()) {
67
+ for (const points of nameMap.values()) {
68
+ points.sort((a, b) => a.ts - b.ts);
69
+ }
70
+ }
71
+ return byType;
72
+ }
73
+ /** Human-friendly label for a metric type. */
74
+ function typeTitle(mtype) {
75
+ switch (mtype) {
76
+ case 'sum':
77
+ return 'Counters (sum)';
78
+ case 'histogram':
79
+ return 'Histograms';
80
+ case 'gauge':
81
+ return 'Gauges';
82
+ case 'exponentialHistogram':
83
+ return 'Exponential Histograms';
84
+ default:
85
+ return mtype.charAt(0).toUpperCase() + mtype.slice(1);
86
+ }
87
+ }
88
+ /** Label variant for the type badge. */
89
+ function typeVariant(mtype) {
90
+ switch (mtype) {
91
+ case 'gauge':
92
+ return 'accent';
93
+ case 'sum':
94
+ return 'attention';
95
+ case 'histogram':
96
+ return 'secondary';
97
+ case 'exponentialHistogram':
98
+ return 'success';
99
+ default:
100
+ return 'primary';
101
+ }
102
+ }
103
+ /** Preferred display order. Types not listed appear at the end. */
104
+ const TYPE_ORDER = {
105
+ sum: 0,
106
+ histogram: 1,
107
+ gauge: 2,
108
+ exponentialHistogram: 3,
109
+ };
110
+ // ── Build ECharts option per type ──────────────────────────────────
111
+ function buildOption(mtype, nameMap) {
112
+ const names = [...nameMap.keys()];
113
+ const baseLegend = {
114
+ data: names,
115
+ top: 6,
116
+ textStyle: { fontSize: 11 },
117
+ };
118
+ const baseGrid = { left: 50, right: 20, top: 40, bottom: 30 };
119
+ const baseTooltip = {
120
+ trigger: 'axis',
121
+ axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } },
122
+ };
123
+ const baseXAxis = {
124
+ type: 'time',
125
+ boundaryGap: false,
126
+ axisLine: { lineStyle: { color: '#ccc' } },
127
+ axisLabel: { fontSize: 10 },
128
+ };
129
+ const baseYAxis = {
130
+ type: 'value',
131
+ splitLine: { lineStyle: { type: 'dashed', color: '#e8e8e8' } },
132
+ axisLine: { show: false },
133
+ axisLabel: { fontSize: 10 },
134
+ };
135
+ if (mtype === 'histogram' || mtype === 'exponentialHistogram') {
136
+ // ── Bar chart ──
137
+ const series = names.map((name, idx) => {
138
+ const palette = SERIES_COLORS[idx % SERIES_COLORS.length];
139
+ const points = nameMap.get(name) ?? [];
140
+ return {
141
+ name,
142
+ type: 'bar',
143
+ barMaxWidth: 20,
144
+ itemStyle: {
145
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
146
+ { offset: 0, color: palette.gradientStart },
147
+ { offset: 1, color: palette.line },
148
+ ]),
149
+ borderRadius: [2, 2, 0, 0],
150
+ },
151
+ emphasis: { focus: 'series' },
152
+ data: points.map(p => [p.ts, p.value]),
153
+ };
154
+ });
155
+ return {
156
+ tooltip: baseTooltip,
157
+ legend: baseLegend,
158
+ grid: baseGrid,
159
+ xAxis: { ...baseXAxis, boundaryGap: true },
160
+ yAxis: baseYAxis,
161
+ series,
162
+ };
163
+ }
164
+ if (mtype === 'gauge') {
165
+ // ── Simple line chart (no area) ──
166
+ const series = names.map((name, idx) => {
167
+ const palette = SERIES_COLORS[idx % SERIES_COLORS.length];
168
+ const points = nameMap.get(name) ?? [];
169
+ return {
170
+ name,
171
+ type: 'line',
172
+ smooth: true,
173
+ symbol: 'circle',
174
+ symbolSize: 5,
175
+ showSymbol: true,
176
+ lineStyle: { width: 2, color: palette.line },
177
+ itemStyle: { color: palette.line },
178
+ emphasis: { focus: 'series' },
179
+ data: points.map(p => [p.ts, p.value]),
180
+ };
181
+ });
182
+ return {
183
+ tooltip: baseTooltip,
184
+ legend: baseLegend,
185
+ grid: baseGrid,
186
+ xAxis: baseXAxis,
187
+ yAxis: baseYAxis,
188
+ series,
189
+ };
190
+ }
191
+ // ── Default (sum / other): area-line with gradient ──
192
+ const series = names.map((name, idx) => {
193
+ const palette = SERIES_COLORS[idx % SERIES_COLORS.length];
194
+ const points = nameMap.get(name) ?? [];
195
+ return {
196
+ name,
197
+ type: 'line',
198
+ smooth: true,
199
+ symbol: 'circle',
200
+ symbolSize: 5,
201
+ showSymbol: false,
202
+ lineStyle: { width: 2, color: palette.line },
203
+ itemStyle: { color: palette.line },
204
+ areaStyle: {
205
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
206
+ { offset: 0, color: palette.gradientStart },
207
+ { offset: 1, color: palette.gradientEnd },
208
+ ]),
209
+ },
210
+ emphasis: { focus: 'series' },
211
+ data: points.map(p => [p.ts, p.value]),
212
+ };
213
+ });
214
+ return {
215
+ tooltip: baseTooltip,
216
+ legend: baseLegend,
217
+ grid: baseGrid,
218
+ xAxis: baseXAxis,
219
+ yAxis: baseYAxis,
220
+ series,
221
+ };
222
+ }
223
+ function computeStats(nameMap) {
224
+ let total = 0;
225
+ let count = 0;
226
+ let startTs = null;
227
+ let endTs = null;
228
+ for (const points of nameMap.values()) {
229
+ for (const p of points) {
230
+ total += p.value;
231
+ count += 1;
232
+ if (startTs === null || p.ts < startTs)
233
+ startTs = p.ts;
234
+ if (endTs === null || p.ts > endTs)
235
+ endTs = p.ts;
236
+ }
237
+ }
238
+ return { total, count, startTs, endTs };
239
+ }
240
+ function formatTs(ts) {
241
+ return new Date(ts).toLocaleTimeString(undefined, {
242
+ hour: '2-digit',
243
+ minute: '2-digit',
244
+ second: '2-digit',
245
+ });
246
+ }
247
+ function formatDuration(ms) {
248
+ if (ms < 1000)
249
+ return `${ms}ms`;
250
+ const s = Math.round(ms / 1000);
251
+ if (s < 60)
252
+ return `${s}s`;
253
+ const m = Math.floor(s / 60);
254
+ const rem = s % 60;
255
+ return rem > 0 ? `${m}m ${rem}s` : `${m}m`;
256
+ }
257
+ // ── Component ───────────────────────────────────────────────────────
258
+ export const OtelMetricsChart = ({ metrics, height = 240, }) => {
259
+ if (!metrics || metrics.length === 0) {
260
+ return (_jsx(ReactECharts, { echarts: echarts, option: {
261
+ title: {
262
+ text: 'No metrics data',
263
+ left: 'center',
264
+ top: 'center',
265
+ textStyle: { color: '#999', fontSize: 14, fontWeight: 'normal' },
266
+ },
267
+ }, style: { width: '100%', height: `${height}px` }, notMerge: true }));
268
+ }
269
+ const byType = groupByType(metrics);
270
+ // Sort types in a stable, user-friendly order.
271
+ const sortedTypes = [...byType.keys()].sort((a, b) => (TYPE_ORDER[a] ?? 99) - (TYPE_ORDER[b] ?? 99));
272
+ return (_jsx(Box, { sx: {
273
+ display: 'grid',
274
+ gridTemplateColumns: '1fr 1fr',
275
+ gap: 2,
276
+ }, children: sortedTypes.map(mtype => {
277
+ const nameMap = byType.get(mtype);
278
+ const option = buildOption(mtype, nameMap);
279
+ const stats = computeStats(nameMap);
280
+ const durationMs = stats.startTs !== null && stats.endTs !== null
281
+ ? stats.endTs - stats.startTs
282
+ : null;
283
+ return (_jsxs(Box, { children: [_jsxs(Box, { sx: {
284
+ display: 'flex',
285
+ alignItems: 'center',
286
+ gap: 2,
287
+ px: 1,
288
+ mb: 1,
289
+ }, children: [_jsx(Label, { size: "small", variant: typeVariant(mtype), children: mtype }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'fg.muted' }, children: typeTitle(mtype) }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["(", [...nameMap.keys()].join(', '), ")"] })] }), _jsxs(Box, { sx: {
290
+ display: 'flex',
291
+ alignItems: 'center',
292
+ gap: 3,
293
+ px: 1,
294
+ mb: 1,
295
+ }, children: [_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'bold', color: 'fg.default' }, children: "Sum:" }), ' ', stats.total.toLocaleString(undefined, {
296
+ maximumFractionDigits: 4,
297
+ })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'bold', color: 'fg.default' }, children: "Points:" }), ' ', stats.count] }), stats.startTs !== null && stats.endTs !== null && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'bold', color: 'fg.default' }, children: "Interval:" }), ' ', formatTs(stats.startTs), " \u2192 ", formatTs(stats.endTs), durationMs !== null && durationMs > 0 && (_jsxs(Text, { as: "span", sx: { color: 'fg.subtle' }, children: [' ', "(", formatDuration(durationMs), ")"] }))] }))] }), _jsx(ReactECharts, { echarts: echarts, option: option, style: { width: '100%', height: `${height}px` }, notMerge: true })] }, mtype));
298
+ }) }));
299
+ };
300
+ export default OtelMetricsChart;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * OtelMetricsList – Tabular view of metric data points grouped by
3
+ * metric name, with expandable rows showing individual data points.
4
+ *
5
+ * Uses Primer React components for consistent theming.
6
+ *
7
+ * @module otel/OtelMetricsList
8
+ */
9
+ import React from 'react';
10
+ import type { OtelMetric } from '../types';
11
+ export interface OtelMetricsListProps {
12
+ metrics: OtelMetric[];
13
+ loading?: boolean;
14
+ }
15
+ export declare const OtelMetricsList: React.FC<OtelMetricsListProps>;
@@ -0,0 +1,213 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2023-2025 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * OtelMetricsList – Tabular view of metric data points grouped by
8
+ * metric name, with expandable rows showing individual data points.
9
+ *
10
+ * Uses Primer React components for consistent theming.
11
+ *
12
+ * @module otel/OtelMetricsList
13
+ */
14
+ import { useMemo, useState } from 'react';
15
+ import { Box, Text, Label, Spinner, CounterLabel, SegmentedControl, } from '@primer/react';
16
+ import { Blankslate } from '@primer/react/experimental';
17
+ import { MeterIcon, ChevronDownIcon, ChevronRightIcon, GraphIcon, TableIcon, } from '@primer/octicons-react';
18
+ import { formatTime } from '../utils';
19
+ import { OtelMetricsChart } from './OtelMetricsChart';
20
+ // ── Helpers ─────────────────────────────────────────────────────────
21
+ /** Group metrics by metric_name. */
22
+ function groupByName(metrics) {
23
+ const map = new Map();
24
+ for (const m of metrics) {
25
+ const key = m.metric_name || '(unnamed)';
26
+ if (!map.has(key))
27
+ map.set(key, []);
28
+ const group = map.get(key);
29
+ if (group)
30
+ group.push(m);
31
+ }
32
+ return map;
33
+ }
34
+ /** Format a metric value with optional unit. */
35
+ function formatValue(value, unit) {
36
+ const formatted = Number.isInteger(value) ? String(value) : value.toFixed(3);
37
+ return unit ? `${formatted} ${unit}` : formatted;
38
+ }
39
+ /** Map metric_type to a Primer Label variant. */
40
+ function typeVariant(type) {
41
+ switch (type?.toLowerCase()) {
42
+ case 'gauge':
43
+ return 'accent';
44
+ case 'counter':
45
+ case 'sum':
46
+ return 'attention';
47
+ case 'histogram':
48
+ return 'secondary';
49
+ case 'exponentialhistogram':
50
+ return 'success';
51
+ default:
52
+ return 'primary';
53
+ }
54
+ }
55
+ // ── MetricGroup ────────────────────────────────────────────────────
56
+ const MetricGroup = ({ name, points }) => {
57
+ const [expanded, setExpanded] = useState(false);
58
+ const first = points[0];
59
+ // Compute stats
60
+ const stats = useMemo(() => {
61
+ const values = points.map(p => p.value);
62
+ const min = Math.min(...values);
63
+ const max = Math.max(...values);
64
+ const avg = values.reduce((a, b) => a + b, 0) / values.length;
65
+ const latest = values[values.length - 1];
66
+ const timestamps = points
67
+ .map(p => new Date(p.timestamp).getTime())
68
+ .filter(t => !isNaN(t));
69
+ const startTs = timestamps.length > 0 ? Math.min(...timestamps) : null;
70
+ const endTs = timestamps.length > 0 ? Math.max(...timestamps) : null;
71
+ return { min, max, avg, latest, startTs, endTs };
72
+ }, [points]);
73
+ return (_jsxs(Box, { sx: {
74
+ borderBottom: '1px solid',
75
+ borderColor: 'border.default',
76
+ }, children: [_jsxs(Box, { onClick: () => setExpanded(!expanded), sx: {
77
+ display: 'grid',
78
+ gridTemplateColumns: '20px 1fr auto auto auto auto',
79
+ gap: 3,
80
+ alignItems: 'center',
81
+ px: 3,
82
+ py: 2,
83
+ cursor: 'pointer',
84
+ ':hover': { bg: 'canvas.subtle' },
85
+ }, children: [expanded ? (_jsx(ChevronDownIcon, { size: 16 })) : (_jsx(ChevronRightIcon, { size: 16 })), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: name }), first?.metric_type && (_jsx(Label, { size: "small", variant: typeVariant(first.metric_type), children: first.metric_type })), first?.unit && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["(", first.unit, ")"] }))] }), stats.startTs !== null && stats.endTs !== null && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle', fontFamily: 'mono' }, children: [formatTime(new Date(stats.startTs).toISOString()), stats.startTs !== stats.endTs && (_jsxs(_Fragment, { children: [" \u2192 ", formatTime(new Date(stats.endTs).toISOString())] }))] }))] }), _jsxs(Box, { sx: { textAlign: 'right' }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "latest" }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', fontFamily: 'mono', ml: 1 }, children: formatValue(stats.latest, first?.unit) })] }), _jsxs(Box, { sx: { textAlign: 'right' }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "avg" }), _jsx(Text, { sx: { fontSize: 1, fontFamily: 'mono', ml: 1 }, children: stats.avg.toFixed(2) })] }), _jsxs(Box, { sx: { textAlign: 'right' }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "min/max" }), _jsxs(Text, { sx: { fontSize: 1, fontFamily: 'mono', ml: 1 }, children: [stats.min.toFixed(1), "\u2013", stats.max.toFixed(1)] })] }), _jsx(CounterLabel, { children: points.length })] }), expanded && (_jsxs(Box, { sx: { bg: 'canvas.subtle' }, children: [_jsxs(Box, { sx: {
86
+ display: 'grid',
87
+ gridTemplateColumns: '20px 180px 1fr 140px 160px',
88
+ gap: 3,
89
+ px: 3,
90
+ py: 1,
91
+ borderTop: '1px solid',
92
+ borderColor: 'border.muted',
93
+ }, children: [_jsx(Box, {}), _jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "Time" }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "Service" }), _jsx(Text, { sx: {
94
+ fontSize: 0,
95
+ fontWeight: 'bold',
96
+ color: 'fg.muted',
97
+ textAlign: 'right',
98
+ }, children: "Value" }), _jsx(Text, { sx: {
99
+ fontSize: 0,
100
+ fontWeight: 'bold',
101
+ color: 'fg.muted',
102
+ textAlign: 'right',
103
+ }, children: "Attributes" })] }), points.map((point, idx) => (_jsx(MetricRow, { metric: point }, `${point.timestamp}-${idx}`)))] }))] }));
104
+ };
105
+ // ── MetricRow ───────────────────────────────────────────────────────
106
+ const MetricRow = ({ metric }) => {
107
+ const [showAttrs, setShowAttrs] = useState(false);
108
+ const attrCount = metric.attributes
109
+ ? Object.keys(metric.attributes).length
110
+ : 0;
111
+ return (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
112
+ display: 'grid',
113
+ gridTemplateColumns: '20px 180px 1fr 140px 160px',
114
+ gap: 3,
115
+ alignItems: 'center',
116
+ px: 3,
117
+ py: 1,
118
+ borderTop: '1px solid',
119
+ borderColor: 'border.muted',
120
+ ':hover': { bg: 'canvas.inset' },
121
+ }, children: [_jsx(Box, {}), _jsx(Text, { sx: { fontSize: 0, fontFamily: 'mono', color: 'fg.muted' }, children: formatTime(metric.timestamp) }), _jsx(Text, { sx: { fontSize: 0 }, children: metric.service_name }), _jsx(Text, { sx: {
122
+ fontSize: 1,
123
+ fontFamily: 'mono',
124
+ fontWeight: 'bold',
125
+ textAlign: 'right',
126
+ }, children: formatValue(metric.value, metric.unit) }), _jsx(Box, { sx: { textAlign: 'right' }, children: attrCount > 0 ? (_jsxs(Label, { size: "small", variant: "secondary", onClick: (e) => {
127
+ e.stopPropagation();
128
+ setShowAttrs(!showAttrs);
129
+ }, sx: { cursor: 'pointer' }, children: [attrCount, " attr", attrCount !== 1 ? 's' : ''] })) : (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "\u2014" })) })] }), showAttrs && metric.attributes && (_jsx(Box, { sx: { px: 5, py: 2, bg: 'canvas.inset' }, children: _jsx(Box, { as: "pre", sx: {
130
+ m: 0,
131
+ fontSize: 0,
132
+ fontFamily: 'mono',
133
+ whiteSpace: 'pre-wrap',
134
+ wordBreak: 'break-word',
135
+ }, children: JSON.stringify(metric.attributes, null, 2) }) }))] }));
136
+ };
137
+ // ── OtelMetricsList ─────────────────────────────────────────────────
138
+ const COOKIE_KEY = 'otel_metrics_view';
139
+ function readViewCookie() {
140
+ try {
141
+ const match = document.cookie.match(/(?:^|;\s*)otel_metrics_view=([^;]+)/);
142
+ if (match && (match[1] === 'chart' || match[1] === 'table'))
143
+ return match[1];
144
+ }
145
+ catch {
146
+ // ignore
147
+ }
148
+ return 'table';
149
+ }
150
+ function writeViewCookie(v) {
151
+ try {
152
+ document.cookie = `${COOKIE_KEY}=${v};path=/;max-age=31536000`;
153
+ }
154
+ catch {
155
+ // ignore
156
+ }
157
+ }
158
+ export const OtelMetricsList = ({ metrics, loading = false, }) => {
159
+ const [view, setView] = useState(readViewCookie);
160
+ const handleViewChange = (v) => {
161
+ writeViewCookie(v);
162
+ setView(v);
163
+ };
164
+ const grouped = useMemo(() => groupByName(metrics), [metrics]);
165
+ if (loading) {
166
+ return (_jsx(Box, { sx: {
167
+ display: 'flex',
168
+ justifyContent: 'center',
169
+ alignItems: 'center',
170
+ p: 5,
171
+ }, children: _jsx(Spinner, { size: "medium" }) }));
172
+ }
173
+ if (metrics.length === 0) {
174
+ return (_jsxs(Blankslate, { children: [_jsx(Blankslate.Visual, { children: _jsx(MeterIcon, { size: 20 }) }), _jsx(Blankslate.Heading, { children: "No metrics yet" }), _jsx(Blankslate.Description, { children: "Generate some metrics or adjust the filters above." })] }));
175
+ }
176
+ return (_jsxs(Box, { sx: {
177
+ display: 'flex',
178
+ flexDirection: 'column',
179
+ flex: 1,
180
+ minHeight: 0,
181
+ overflow: 'hidden',
182
+ }, children: [_jsx(Box, { sx: {
183
+ flexShrink: 0,
184
+ display: 'flex',
185
+ alignItems: 'center',
186
+ justifyContent: 'flex-end',
187
+ px: 3,
188
+ py: 2,
189
+ bg: 'canvas.subtle',
190
+ borderBottom: '1px solid',
191
+ borderColor: 'border.default',
192
+ zIndex: 0,
193
+ }, children: _jsxs(SegmentedControl, { "aria-label": "Metrics view", size: "small", onChange: (idx) => handleViewChange(idx === 0 ? 'chart' : 'table'), children: [_jsx(SegmentedControl.IconButton, { icon: GraphIcon, "aria-label": "Chart", selected: view === 'chart' }), _jsx(SegmentedControl.IconButton, { icon: TableIcon, "aria-label": "Table", selected: view === 'table' })] }) }), _jsx(Box, { sx: { flex: 1, minHeight: 0, overflow: 'auto' }, children: view === 'chart' ? (_jsx(Box, { sx: { px: 3, py: 2 }, children: _jsx(OtelMetricsChart, { metrics: metrics, height: 280 }) })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
194
+ display: 'grid',
195
+ gridTemplateColumns: '20px 1fr auto auto auto auto',
196
+ gap: 3,
197
+ px: 3,
198
+ py: 1,
199
+ bg: 'canvas.subtle',
200
+ borderBottom: '2px solid',
201
+ borderColor: 'border.default',
202
+ position: 'sticky',
203
+ top: 0,
204
+ zIndex: 1,
205
+ }, children: [_jsx(Box, {}), ['Metric Name', 'Latest', 'Average', 'Min/Max', 'Points'].map((h, i) => (_jsx(Text, { sx: {
206
+ fontSize: 0,
207
+ fontWeight: 'bold',
208
+ color: 'fg.muted',
209
+ textTransform: 'uppercase',
210
+ letterSpacing: '0.05em',
211
+ textAlign: i === 0 ? undefined : 'right',
212
+ }, children: h }, h)))] }), [...grouped.entries()].map(([name, points]) => (_jsx(MetricGroup, { name: name, points: points }, name)))] })) })] }));
213
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OtelSearchBar – Filter toolbar with signal-type tabs, service selector,
3
+ * query input, and refresh action.
4
+ *
5
+ * Uses Primer React components for consistent theming.
6
+ *
7
+ * @module otel/OtelSearchBar
8
+ */
9
+ import React from 'react';
10
+ import type { OtelSearchBarProps } from '../types';
11
+ export declare const OtelSearchBar: React.FC<OtelSearchBarProps>;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, SegmentedControl, ActionMenu, ActionList, TextInput, Button, } from '@primer/react';
3
+ import { SyncIcon, SearchIcon } from '@primer/octicons-react';
4
+ const SIGNALS = [
5
+ { value: 'traces', label: 'Traces' },
6
+ { value: 'logs', label: 'Logs' },
7
+ { value: 'metrics', label: 'Metrics' },
8
+ ];
9
+ export const OtelSearchBar = ({ signal, onSignalChange, services, selectedService, onServiceChange, query, onQueryChange, onRefresh, loading, }) => {
10
+ const safeServices = Array.isArray(services) ? services : [];
11
+ return (_jsxs(Box, { sx: {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: 2,
15
+ px: 3,
16
+ py: 2,
17
+ bg: 'canvas.subtle',
18
+ borderBottom: '1px solid',
19
+ borderColor: 'border.default',
20
+ flexWrap: 'wrap',
21
+ }, children: [_jsx(SegmentedControl, { "aria-label": "Signal type", size: "small", onChange: idx => onSignalChange(SIGNALS[idx].value), children: SIGNALS.map(s => (_jsx(SegmentedControl.Button, { selected: s.value === signal, "aria-label": s.label, children: s.label }, s.value))) }), _jsxs(ActionMenu, { children: [_jsx(ActionMenu.Button, { size: "small", variant: "invisible", children: selectedService || 'All services' }), _jsx(ActionMenu.Overlay, { width: "auto", children: _jsxs(ActionList, { selectionVariant: "single", children: [_jsx(ActionList.Item, { selected: selectedService === '', onSelect: () => onServiceChange(''), children: "All services" }), safeServices.length > 0 && _jsx(ActionList.Divider, {}), safeServices.map(svc => (_jsx(ActionList.Item, { selected: selectedService === svc, onSelect: () => onServiceChange(svc), children: svc }, svc)))] }) })] }), _jsx(Box, { sx: { flex: 1, minWidth: 180 }, children: _jsx(TextInput, { leadingVisual: SearchIcon, value: query, onChange: e => onQueryChange(e.target.value), placeholder: "Search spans, logs, metrics\u2026", size: "small", block: true, "aria-label": "Search telemetry" }) }), onRefresh && (_jsx(Button, { size: "small", leadingVisual: SyncIcon, onClick: onRefresh, disabled: loading, "aria-label": "Refresh", children: "Refresh" }))] }));
22
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OtelSpanDetail – Detail panel for a selected span, with metadata,
3
+ * collapsible attributes, gen_ai arguments, events, and links.
4
+ *
5
+ * Uses Primer React components for consistent theming.
6
+ *
7
+ * @module otel/OtelSpanDetail
8
+ */
9
+ import React from 'react';
10
+ import type { OtelSpanDetailProps } from '../types';
11
+ export declare const OtelSpanDetail: React.FC<OtelSpanDetailProps>;