@datalayer/core 1.0.3 → 1.0.12

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 (117) hide show
  1. package/lib/api/constants.d.ts +3 -0
  2. package/lib/api/constants.js +3 -0
  3. package/lib/api/runtimes/checkpoints.d.ts +122 -0
  4. package/lib/api/runtimes/checkpoints.js +118 -0
  5. package/lib/api/runtimes/index.d.ts +1 -0
  6. package/lib/api/runtimes/index.js +1 -0
  7. package/lib/api/runtimes/runtimes.d.ts +84 -0
  8. package/lib/api/runtimes/runtimes.js +50 -0
  9. package/lib/components/auth/Login.js +1 -1
  10. package/lib/components/display/BusyDots.d.ts +9 -0
  11. package/lib/components/display/BusyDots.js +31 -0
  12. package/lib/components/display/LiveRelativeTime.d.ts +10 -0
  13. package/lib/components/display/LiveRelativeTime.js +21 -0
  14. package/lib/components/display/index.d.ts +2 -0
  15. package/lib/components/display/index.js +2 -0
  16. package/lib/components/flashes/FlashSurveys.js +1 -1
  17. package/lib/components/index.d.ts +1 -0
  18. package/lib/components/index.js +1 -0
  19. package/lib/components/navbar/SubdomainNavBar.js +1 -1
  20. package/lib/components/progress/ConsumptionBar.js +6 -7
  21. package/lib/components/progress/CreditsIndicator.js +2 -2
  22. package/lib/components/progress/consumption.d.ts +12 -0
  23. package/lib/components/progress/consumption.js +31 -0
  24. package/lib/components/progress/index.d.ts +1 -0
  25. package/lib/components/progress/index.js +1 -0
  26. package/lib/components/sparklines/Sparklines.d.ts +16 -0
  27. package/lib/components/sparklines/Sparklines.js +65 -0
  28. package/lib/components/sparklines/SparklinesLine.d.ts +8 -0
  29. package/lib/components/sparklines/SparklinesLine.js +37 -0
  30. package/lib/components/sparklines/dataProcessing.d.ts +25 -0
  31. package/lib/components/sparklines/dataProcessing.js +35 -0
  32. package/lib/components/sparklines/index.d.ts +4 -0
  33. package/lib/components/sparklines/index.js +7 -0
  34. package/lib/components/sparklines/types.d.ts +36 -0
  35. package/lib/components/sparklines/types.js +5 -0
  36. package/lib/components/storage/ContentsBrowser.js +17 -1
  37. package/lib/components/subnav/SubNav.js +1 -1
  38. package/lib/hooks/index.d.ts +3 -0
  39. package/lib/hooks/index.js +3 -0
  40. package/lib/hooks/useCache.d.ts +5 -67
  41. package/lib/hooks/useCache.js +15 -213
  42. package/lib/hooks/useHighZIndexPortal.d.ts +4 -0
  43. package/lib/hooks/useHighZIndexPortal.js +36 -0
  44. package/lib/hooks/useKeyboardShortcuts.d.ts +23 -0
  45. package/lib/hooks/useKeyboardShortcuts.js +124 -0
  46. package/lib/hooks/useProjects.d.ts +1 -1
  47. package/lib/models/ItemDTO.js +1 -1
  48. package/lib/models/RolesPlatform.js +2 -2
  49. package/lib/models/User.d.ts +2 -0
  50. package/lib/models/User.js +4 -1
  51. package/lib/otel/client/OtelClient.d.ts +93 -0
  52. package/lib/otel/client/OtelClient.js +232 -0
  53. package/lib/otel/client/index.d.ts +2 -0
  54. package/lib/otel/client/index.js +5 -0
  55. package/lib/otel/{hooks.d.ts → hooks/index.d.ts} +15 -1
  56. package/lib/otel/{hooks.js → hooks/index.js} +58 -16
  57. package/lib/otel/index.d.ts +29 -20
  58. package/lib/otel/index.js +19 -15
  59. package/lib/otel/{OtelLive.d.ts → views/OtelLive.d.ts} +1 -1
  60. package/lib/otel/{OtelLive.js → views/OtelLive.js} +22 -4
  61. package/lib/otel/{OtelLogsList.d.ts → views/OtelLogsList.d.ts} +1 -1
  62. package/lib/otel/{OtelLogsList.js → views/OtelLogsList.js} +1 -1
  63. package/lib/otel/{OtelMetricsChart.d.ts → views/OtelMetricsChart.d.ts} +1 -1
  64. package/lib/otel/{OtelMetricsList.d.ts → views/OtelMetricsList.d.ts} +1 -1
  65. package/lib/otel/{OtelMetricsList.js → views/OtelMetricsList.js} +1 -1
  66. package/lib/otel/{OtelSearchBar.d.ts → views/OtelSearchBar.d.ts} +1 -1
  67. package/lib/otel/{OtelSpanDetail.d.ts → views/OtelSpanDetail.d.ts} +1 -1
  68. package/lib/otel/{OtelSpanDetail.js → views/OtelSpanDetail.js} +1 -1
  69. package/lib/otel/{OtelSpanTree.d.ts → views/OtelSpanTree.d.ts} +1 -1
  70. package/lib/otel/{OtelSpanTree.js → views/OtelSpanTree.js} +1 -1
  71. package/lib/otel/{OtelSqlView.js → views/OtelSqlView.js} +1 -1
  72. package/lib/otel/{OtelSystemView.js → views/OtelSystemView.js} +1 -1
  73. package/lib/otel/{OtelTimeline.d.ts → views/OtelTimeline.d.ts} +1 -1
  74. package/lib/otel/{OtelTimeline.js → views/OtelTimeline.js} +1 -1
  75. package/lib/otel/{OtelTimelineRangeSlider.d.ts → views/OtelTimelineRangeSlider.d.ts} +1 -1
  76. package/lib/otel/{OtelTracesList.d.ts → views/OtelTracesList.d.ts} +1 -1
  77. package/lib/otel/{OtelTracesList.js → views/OtelTracesList.js} +1 -1
  78. package/lib/otel/views/index.d.ts +20 -0
  79. package/lib/otel/views/index.js +21 -0
  80. package/lib/state/substates/CoreState.js +6 -6
  81. package/lib/utils/Date.d.ts +6 -0
  82. package/lib/utils/Date.js +37 -0
  83. package/lib/views/iam/SignInSimple.d.ts +5 -0
  84. package/lib/views/iam/SignInSimple.js +39 -6
  85. package/lib/views/iam-tokens/IAMTokenEdit.d.ts +5 -1
  86. package/lib/views/iam-tokens/IAMTokenEdit.js +3 -3
  87. package/lib/views/iam-tokens/IAMTokenNew.js +2 -2
  88. package/lib/views/iam-tokens/IAMTokens.d.ts +4 -2
  89. package/lib/views/iam-tokens/IAMTokens.js +4 -4
  90. package/lib/views/index.d.ts +1 -0
  91. package/lib/views/index.js +1 -0
  92. package/lib/views/otel/DashboardView.d.ts +16 -0
  93. package/lib/views/otel/DashboardView.js +4 -0
  94. package/lib/views/otel/LogsView.d.ts +12 -0
  95. package/lib/views/otel/LogsView.js +4 -0
  96. package/lib/views/otel/MetricsView.d.ts +12 -0
  97. package/lib/views/otel/MetricsView.js +4 -0
  98. package/lib/views/otel/OtelHeader.d.ts +33 -0
  99. package/lib/views/otel/OtelHeader.js +105 -0
  100. package/lib/views/otel/SqlView.d.ts +9 -0
  101. package/lib/views/otel/SqlView.js +4 -0
  102. package/lib/views/otel/SystemView.d.ts +9 -0
  103. package/lib/views/otel/SystemView.js +4 -0
  104. package/lib/views/otel/TracesView.d.ts +12 -0
  105. package/lib/views/otel/TracesView.js +4 -0
  106. package/lib/views/otel/index.d.ts +16 -0
  107. package/lib/views/otel/index.js +12 -0
  108. package/lib/views/otel/simpleAuthStore.d.ts +21 -0
  109. package/lib/views/otel/simpleAuthStore.js +22 -0
  110. package/lib/views/profile/UserBadge.d.ts +2 -0
  111. package/lib/views/profile/UserBadge.js +3 -3
  112. package/package.json +1 -26
  113. /package/lib/otel/{OtelMetricsChart.js → views/OtelMetricsChart.js} +0 -0
  114. /package/lib/otel/{OtelSearchBar.js → views/OtelSearchBar.js} +0 -0
  115. /package/lib/otel/{OtelSqlView.d.ts → views/OtelSqlView.d.ts} +0 -0
  116. /package/lib/otel/{OtelSystemView.d.ts → views/OtelSystemView.d.ts} +0 -0
  117. /package/lib/otel/{OtelTimelineRangeSlider.js → views/OtelTimelineRangeSlider.js} +0 -0
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
14
14
  import { useState, useMemo, useCallback } from 'react';
15
15
  import { Box, Text } from '@primer/react';
16
16
  import { ChevronDownIcon, ChevronRightIcon } from '@primer/octicons-react';
17
- import { formatDuration, serviceColor, toMs } from './utils';
17
+ import { formatDuration, serviceColor, toMs } from '../utils';
18
18
  const INDENT_PX = 20;
19
19
  const SpanNode = ({ span, depth, selectedSpanId, onSelectSpan, expandedIds, toggleExpanded, traceMinTime, traceDuration, }) => {
20
20
  const hasChildren = (span.children?.length ?? 0) > 0;
@@ -16,7 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
16
  import { useState, useCallback } from 'react';
17
17
  import { ActionList, ActionMenu, Box, Button, Text, Textarea, Spinner, } from '@primer/react';
18
18
  import { HistoryIcon, TableIcon, XCircleIcon, DownloadIcon, } from '@primer/octicons-react';
19
- import { useOtelQuery } from './hooks';
19
+ import { useOtelQuery } from '../hooks';
20
20
  // ── Cookie helpers ────────────────────────────────────────────────
21
21
  const HISTORY_COOKIE = 'otel_sql_history';
22
22
  const HISTORY_MAX = 10;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Button, Spinner, Text } from '@primer/react';
3
3
  import { SyncIcon } from '@primer/octicons-react';
4
- import { useOtelSystem } from './hooks';
4
+ import { useOtelSystem } from '../hooks';
5
5
  // ── Helpers ───────────────────────────────────────────────────────
6
6
  function fmtBytes(bytes) {
7
7
  if (bytes >= 1024 ** 3)
@@ -7,5 +7,5 @@
7
7
  * @module otel/OtelTimeline
8
8
  */
9
9
  import React from 'react';
10
- import type { OtelTimelineProps } from './types';
10
+ import type { OtelTimelineProps } from '../types';
11
11
  export declare const OtelTimeline: React.FC<OtelTimelineProps>;
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  */
14
14
  import { useMemo } from 'react';
15
15
  import { Box, Text } from '@primer/react';
16
- import { toMs, formatDuration, serviceColor } from './utils';
16
+ import { toMs, formatDuration, serviceColor } from '../utils';
17
17
  export const OtelTimeline = ({ spans, barHeight = 24, selectedSpanId, onSelectSpan, }) => {
18
18
  const { minTime, maxTime, sortedSpans } = useMemo(() => {
19
19
  if (spans.length === 0)
@@ -12,5 +12,5 @@
12
12
  * @module otel/OtelTimelineRangeSlider
13
13
  */
14
14
  import React from 'react';
15
- import type { OtelTimelineRangeSliderProps } from './types';
15
+ import type { OtelTimelineRangeSliderProps } from '../types';
16
16
  export declare const OtelTimelineRangeSlider: React.FC<OtelTimelineRangeSliderProps>;
@@ -9,5 +9,5 @@
9
9
  * @module otel/OtelTracesList
10
10
  */
11
11
  import React from 'react';
12
- import type { OtelTracesListProps } from './types';
12
+ import type { OtelTracesListProps } from '../types';
13
13
  export declare const OtelTracesList: React.FC<OtelTracesListProps>;
@@ -17,7 +17,7 @@ import { useMemo, useCallback, useState } from 'react';
17
17
  import { Box, Text, Label, Spinner } from '@primer/react';
18
18
  import { Blankslate } from '@primer/react/experimental';
19
19
  import { TelescopeIcon, ChevronRightIcon, ChevronDownIcon, } from '@primer/octicons-react';
20
- import { formatDuration, formatTime, buildSpanTree } from './utils';
20
+ import { formatDuration, formatTime, buildSpanTree } from '../utils';
21
21
  const GRID_COLS = '140px 1fr 160px 90px';
22
22
  // ── Helpers ─────────────────────────────────────────────────────────
23
23
  /** Extract gen_ai token counts from span attributes. */
@@ -0,0 +1,20 @@
1
+ /**
2
+ * OTEL view components.
3
+ *
4
+ * @module otel/views
5
+ */
6
+ export { OtelLive } from './OtelLive';
7
+ export { OtelTracesList } from './OtelTracesList';
8
+ export { OtelLogsList } from './OtelLogsList';
9
+ export { OtelMetricsList } from './OtelMetricsList';
10
+ export { OtelMetricsChart } from './OtelMetricsChart';
11
+ export type { OtelMetricsChartProps } from './OtelMetricsChart';
12
+ export { OtelSearchBar } from './OtelSearchBar';
13
+ export { OtelSpanDetail } from './OtelSpanDetail';
14
+ export { OtelSpanTree } from './OtelSpanTree';
15
+ export { OtelTimeline } from './OtelTimeline';
16
+ export { OtelTimelineRangeSlider } from './OtelTimelineRangeSlider';
17
+ export { OtelSqlView } from './OtelSqlView';
18
+ export type { OtelSqlViewProps } from './OtelSqlView';
19
+ export { OtelSystemView } from './OtelSystemView';
20
+ export type { OtelSystemViewProps } from './OtelSystemView';
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * OTEL view components.
7
+ *
8
+ * @module otel/views
9
+ */
10
+ export { OtelLive } from './OtelLive';
11
+ export { OtelTracesList } from './OtelTracesList';
12
+ export { OtelLogsList } from './OtelLogsList';
13
+ export { OtelMetricsList } from './OtelMetricsList';
14
+ export { OtelMetricsChart } from './OtelMetricsChart';
15
+ export { OtelSearchBar } from './OtelSearchBar';
16
+ export { OtelSpanDetail } from './OtelSpanDetail';
17
+ export { OtelSpanTree } from './OtelSpanTree';
18
+ export { OtelTimeline } from './OtelTimeline';
19
+ export { OtelTimelineRangeSlider } from './OtelTimelineRangeSlider';
20
+ export { OtelSqlView } from './OtelSqlView';
21
+ export { OtelSystemView } from './OtelSystemView';
@@ -17,7 +17,7 @@ let initialConfiguration = {
17
17
  loadConfigurationFromServer: true,
18
18
  jupyterServerless: false,
19
19
  iamRunUrl: 'https://prod1.datalayer.run',
20
- runtimesRunUrl: 'https://prod1.datalayer.run',
20
+ runtimesRunUrl: 'https://r1.datalayer.run',
21
21
  libraryRunUrl: 'https://prod1.datalayer.run',
22
22
  spacerRunUrl: 'https://prod1.datalayer.run',
23
23
  aiagentsRunUrl: 'https://prod1.datalayer.run',
@@ -40,11 +40,11 @@ let initialConfiguration = {
40
40
  logoUrl: 'https://assets.datalayer.tech/datalayer-25.svg',
41
41
  logoSquareUrl: 'https://assets.datalayer.tech/datalayer-square.png',
42
42
  copyright: '© 2025 Datalayer, Inc',
43
- docsUrl: 'https://docs.datalayer.app',
44
- supportUrl: 'https://datalayer.app/support',
45
- termsUrl: 'https://datalayer.app/terms',
46
- pricingUrl: 'https://datalayer.app/pricing',
47
- privacyUrl: 'https://datalayer.app/privacy',
43
+ docsUrl: 'https://docs.datalayer.ai',
44
+ supportUrl: 'https://datalayer.ai/support',
45
+ termsUrl: 'https://datalayer.ai/terms',
46
+ pricingUrl: 'https://datalayer.ai/pricing',
47
+ privacyUrl: 'https://datalayer.ai/privacy',
48
48
  },
49
49
  };
50
50
  // Try loading initial state from datalayer-config-data element
@@ -1,2 +1,8 @@
1
1
  export declare const getRelativeTime: (d1: Date, d2?: Date) => string;
2
2
  export declare const timeSince: (date: Date) => string;
3
+ /**
4
+ * Format a timestamp into a compact relative string.
5
+ *
6
+ * Examples: "just now", "15m ago", "3h ago", "2d ago", "4w ago", "1y ago".
7
+ */
8
+ export declare const formatRelativeTime: (value?: Date | string | number, now?: Date) => string | undefined;
package/lib/utils/Date.js CHANGED
@@ -58,3 +58,40 @@ export const timeSince = (date) => {
58
58
  const i = Math.floor(interval);
59
59
  return i + ` second${p(i)}`;
60
60
  };
61
+ /**
62
+ * Format a timestamp into a compact relative string.
63
+ *
64
+ * Examples: "just now", "15m ago", "3h ago", "2d ago", "4w ago", "1y ago".
65
+ */
66
+ export const formatRelativeTime = (value, now = new Date()) => {
67
+ if (value === undefined || value === null) {
68
+ return undefined;
69
+ }
70
+ const date = value instanceof Date
71
+ ? value
72
+ : typeof value === 'number'
73
+ ? new Date(value)
74
+ : new Date(value);
75
+ const ts = date.getTime();
76
+ if (Number.isNaN(ts)) {
77
+ return typeof value === 'string' ? value : undefined;
78
+ }
79
+ const diffMs = Math.max(0, now.getTime() - ts);
80
+ const seconds = Math.floor(diffMs / 1000);
81
+ if (seconds < 60)
82
+ return 'just now';
83
+ const minutes = Math.floor(seconds / 60);
84
+ if (minutes < 60)
85
+ return `${minutes}m ago`;
86
+ const hours = Math.floor(minutes / 60);
87
+ if (hours < 24)
88
+ return `${hours}h ago`;
89
+ const days = Math.floor(hours / 24);
90
+ if (days < 7)
91
+ return `${days}d ago`;
92
+ const weeks = Math.floor(days / 7);
93
+ if (weeks < 52)
94
+ return `${weeks}w ago`;
95
+ const years = Math.floor(days / 365);
96
+ return `${years}y ago`;
97
+ };
@@ -14,6 +14,11 @@ export interface SignInSimpleProps {
14
14
  * Typically used to store credentials in a Zustand / context store.
15
15
  */
16
16
  onSignIn: (token: string, handle: string) => void;
17
+ /**
18
+ * Called when the user authenticates with an API key.
19
+ * If not provided the "Sign in with API Key" button is hidden.
20
+ */
21
+ onApiKeySignIn?: (apiKey: string) => void;
17
22
  /**
18
23
  * Login endpoint. Defaults to `/api/iam/v1/login`.
19
24
  * The endpoint must accept `POST { handle, password }` and return
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
@@ -12,16 +12,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  *
13
13
  * @module views/signin
14
14
  */
15
- import { useState, useCallback } from 'react';
16
- import { Box, Button, FormControl, Heading, Text, TextInput, } from '@primer/react';
17
- import { EyeIcon, EyeClosedIcon, TelescopeIcon } from '@primer/octicons-react';
15
+ import { useState, useCallback, useRef } from 'react';
16
+ import { Box, Button, FormControl, Heading, Text, Textarea, TextInput, } from '@primer/react';
17
+ import { Dialog } from '@primer/react/experimental';
18
+ import { EyeIcon, EyeClosedIcon, KeyIcon, TelescopeIcon, } from '@primer/octicons-react';
18
19
  // ── Component ────────────────────────────────────────────────────────
19
- export const SignInSimple = ({ onSignIn, loginUrl = '/api/iam/v1/login', title = 'Datalayer OTEL', description = 'Sign in to access the observability dashboard.', leadingIcon = _jsx(TelescopeIcon, { size: 24 }), }) => {
20
+ export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl = '/api/iam/v1/login', title = 'Datalayer OTEL', description = 'Sign in to access the observability dashboard.', leadingIcon = _jsx(TelescopeIcon, { size: 24 }), }) => {
20
21
  const [handle, setHandle] = useState('');
21
22
  const [password, setPassword] = useState('');
22
23
  const [showPassword, setShowPassword] = useState(false);
23
24
  const [loading, setLoading] = useState(false);
24
25
  const [error, setError] = useState(null);
26
+ // API Key dialog state
27
+ const [showApiKeyDialog, setShowApiKeyDialog] = useState(false);
28
+ const [apiKey, setApiKey] = useState('');
29
+ const apiKeyRef = useRef(null);
25
30
  const submit = useCallback(async () => {
26
31
  if (!handle || !password || loading)
27
32
  return;
@@ -75,6 +80,34 @@ export const SignInSimple = ({ onSignIn, loginUrl = '/api/iam/v1/login', title =
75
80
  gap: 2,
76
81
  mb: 4,
77
82
  justifyContent: 'center',
78
- }, children: [leadingIcon, _jsx(Heading, { sx: { fontSize: 3 }, children: title })] }), _jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', mb: 3, textAlign: 'center' }, children: description }), _jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Username" }), _jsx(TextInput, { autoFocus: true, block: true, placeholder: "Your username", value: handle, onChange: e => setHandle(e.target.value), onKeyDown: handleKeyDown })] }), _jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Password" }), _jsx(TextInput, { block: true, placeholder: "Your password", type: showPassword ? 'text' : 'password', value: password, onChange: e => setPassword(e.target.value), onKeyDown: handleKeyDown, trailingAction: _jsx(TextInput.Action, { onClick: () => setShowPassword(!showPassword), icon: showPassword ? EyeClosedIcon : EyeIcon, "aria-label": showPassword ? 'Hide password' : 'Show password', sx: { color: 'var(--fgColor-muted)' } }) })] }), error && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 1, mb: 3, display: 'block' }, children: error })), _jsx(Button, { variant: "primary", block: true, disabled: loading || !handle || !password, onClick: submit, children: loading ? 'Signing in…' : 'Sign in' })] }) }));
83
+ }, children: [leadingIcon, _jsx(Heading, { sx: { fontSize: 3 }, children: title })] }), _jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', mb: 3, textAlign: 'center' }, children: description }), _jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Username" }), _jsx(TextInput, { autoFocus: true, block: true, placeholder: "Your username", value: handle, onChange: e => setHandle(e.target.value), onKeyDown: handleKeyDown })] }), _jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Password" }), _jsx(TextInput, { block: true, placeholder: "Your password", type: showPassword ? 'text' : 'password', value: password, onChange: e => setPassword(e.target.value), onKeyDown: handleKeyDown, trailingAction: _jsx(TextInput.Action, { onClick: () => setShowPassword(!showPassword), icon: showPassword ? EyeClosedIcon : EyeIcon, "aria-label": showPassword ? 'Hide password' : 'Show password', sx: { color: 'var(--fgColor-muted)' } }) })] }), error && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 1, mb: 3, display: 'block' }, children: error })), _jsx(Button, { variant: "primary", block: true, disabled: loading || !handle || !password, onClick: submit, children: loading ? 'Signing in…' : 'Sign in' }), onApiKeySignIn && (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
84
+ display: 'flex',
85
+ alignItems: 'center',
86
+ gap: 2,
87
+ my: 3,
88
+ }, children: [_jsx(Box, { sx: { flex: 1, height: '1px', bg: 'border.default' } }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "or" }), _jsx(Box, { sx: { flex: 1, height: '1px', bg: 'border.default' } })] }), _jsx(Button, { block: true, leadingVisual: KeyIcon, onClick: () => setShowApiKeyDialog(true), children: "Sign in with API Key" }), showApiKeyDialog && (_jsx(Dialog, { title: "Enter your API Key", onClose: () => {
89
+ setShowApiKeyDialog(false);
90
+ setApiKey('');
91
+ }, initialFocusRef: apiKeyRef, footerButtons: [
92
+ {
93
+ buttonType: 'default',
94
+ onClick: () => {
95
+ setShowApiKeyDialog(false);
96
+ setApiKey('');
97
+ },
98
+ content: 'Cancel',
99
+ },
100
+ {
101
+ buttonType: 'primary',
102
+ onClick: () => {
103
+ if (apiKey.trim()) {
104
+ onApiKeySignIn(apiKey.trim());
105
+ setShowApiKeyDialog(false);
106
+ setApiKey('');
107
+ }
108
+ },
109
+ content: 'Authenticate',
110
+ },
111
+ ], children: _jsx(Box, { as: "form", children: _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "API Key" }), _jsx(Textarea, { block: true, required: true, autoFocus: true, placeholder: "Paste your API key here", value: apiKey, onInput: (e) => setApiKey(e.target.value), ref: apiKeyRef })] }) }) }))] }))] }) }));
79
112
  };
80
113
  export default SignInSimple;
@@ -1,2 +1,6 @@
1
- export declare const IAMTokenEdit: () => import("react/jsx-runtime").JSX.Element;
1
+ export type IAMTokenEditProps = {
2
+ /** Route to navigate after delete. Defaults to '/settings/iam/tokens'. */
3
+ tokensListRoute?: string;
4
+ };
5
+ export declare const IAMTokenEdit: ({ tokensListRoute, }?: IAMTokenEditProps) => import("react/jsx-runtime").JSX.Element;
2
6
  export default IAMTokenEdit;
@@ -10,7 +10,7 @@ import { Box } from '@datalayer/primer-addons';
10
10
  import { BoringAvatar } from '../../components/avatars';
11
11
  import { useCache, useNavigate, useToast } from '../../hooks';
12
12
  import { useRunStore } from '../../state';
13
- export const IAMTokenEdit = () => {
13
+ export const IAMTokenEdit = ({ tokensListRoute = '/settings/iam/tokens', } = {}) => {
14
14
  const { tokenId } = useParams();
15
15
  const runStore = useRunStore();
16
16
  const navigate = useNavigate();
@@ -100,7 +100,7 @@ export const IAMTokenEdit = () => {
100
100
  enqueueToast('The token is successfully deleted.', {
101
101
  variant: 'success',
102
102
  });
103
- navigate('/settings/iam/tokens');
103
+ navigate(tokensListRoute);
104
104
  }
105
105
  else {
106
106
  enqueueToast(resp.message || 'Failed to delete token.', {
@@ -116,7 +116,7 @@ export const IAMTokenEdit = () => {
116
116
  },
117
117
  });
118
118
  };
119
- return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "IAM Token" }) }), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: token?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: token?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: token?.variant }) })] }), _jsxs(Box, { ml: 10, children: [_jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: nameNameChange }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: nameDescriptionChange, rows: 5 }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Expiration date" }), _jsx(TextInput, { block: true, value: token?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: nameSubmit, children: "Update token" })] }), _jsxs(Box, { sx: { marginTop: 3 }, children: [_jsx(Heading, { as: "h2", sx: {
119
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "API Key" }) }), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: token?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: token?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: token?.variant }) })] }), _jsxs(Box, { ml: 10, children: [_jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: nameNameChange }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: nameDescriptionChange, rows: 5 }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Expiration date" }), _jsx(TextInput, { block: true, value: token?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: nameSubmit, children: "Update token" })] }), _jsxs(Box, { sx: { marginTop: 3 }, children: [_jsx(Heading, { as: "h2", sx: {
120
120
  fontSize: 4,
121
121
  fontWeight: 'normal',
122
122
  color: 'danger.fg',
@@ -95,7 +95,7 @@ export const IAMTokenNew = ({ tokensListRoute = '/settings/iam/tokens', } = {})
95
95
  },
96
96
  });
97
97
  };
98
- return (_jsx(Box, { children: showToken ? (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Your Token is created" }) }) }), _jsx(Box, { children: _jsx(Text, { children: "Take note of the Token value, you won't be able to see it after." }) }), _jsx(Box, { children: _jsxs(Text, { children: ["Name: ", token?.name] }) }), _jsx(Box, { children: _jsxs(Text, { children: ["Description: ", token?.description] }) }), _jsx(Box, { children: _jsxs(Text, { children: ["Expiration date: ", token?.expirationDate.toISOString()] }) }), _jsxs(Box, { children: [_jsx(Text, { mb: 2, children: "Value: " }), _jsxs(Box, { display: "flex", sx: { alignItems: 'center', gap: 2 }, children: [_jsx(Text, { as: "code", sx: {
98
+ return (_jsx(Box, { children: showToken ? (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Your API Key is created" }) }) }), _jsx(Box, { children: _jsx(Text, { children: "Take note of the API Key value, you will not be able to see it after." }) }), _jsx(Box, { children: _jsxs(Text, { children: ["Name: ", token?.name] }) }), _jsx(Box, { children: _jsxs(Text, { children: ["Description: ", token?.description] }) }), _jsx(Box, { children: _jsxs(Text, { children: ["Expiration date: ", token?.expirationDate.toISOString()] }) }), _jsxs(Box, { children: [_jsx(Text, { mb: 2, children: "Value: " }), _jsxs(Box, { display: "flex", sx: { alignItems: 'center', gap: 2 }, children: [_jsx(Text, { as: "code", sx: {
99
99
  color: 'fg.onEmphasis',
100
100
  bg: 'neutral.emphasis',
101
101
  p: 2,
@@ -108,7 +108,7 @@ export const IAMTokenNew = ({ tokensListRoute = '/settings/iam/tokens', } = {})
108
108
  variant: 'success',
109
109
  });
110
110
  }
111
- } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(tokensListRoute, e), children: "List my Tokens" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New IAM Token" }) }) }), _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Token type" }), _jsx(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: _jsx(Select.Option, { value: "user_token", children: "User Token" }) }), _jsx(FormControl.Caption, { children: "Pick the most appropriate token type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The token name is a short name that identifies in a unique way your token." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Expiration day" }), _jsx(Calendar, { showGoToToday: true, onSelectDate: expirationDateChange, value: formValues.expirationDate, strings: defaultCalendarStrings }), validationResult.expirationDate !== true ? (_jsx(FormControl.Validation, { variant: "error", children: "Pick an expiration date in the future." })) : (_jsxs(FormControl.Validation, { variant: "success", children: ["Expiration date:", ' ', formValues.expirationDate?.toLocaleDateString(), "."] }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name ||
111
+ } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(tokensListRoute, e), children: "List my API Keys" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New API Key" }) }) }), _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Token type" }), _jsx(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: _jsx(Select.Option, { value: "user_token", children: "User Token" }) }), _jsx(FormControl.Caption, { children: "Pick the most appropriate token type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The token name is a short name that identifies in a unique way your token." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Expiration day" }), _jsx(Calendar, { showGoToToday: true, onSelectDate: expirationDateChange, value: formValues.expirationDate, strings: defaultCalendarStrings }), validationResult.expirationDate !== true ? (_jsx(FormControl.Validation, { variant: "error", children: "Pick an expiration date in the future." })) : (_jsxs(FormControl.Validation, { variant: "success", children: ["Expiration date:", ' ', formValues.expirationDate?.toLocaleDateString(), "."] }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name ||
112
112
  !validationResult.description ||
113
113
  !validationResult.expirationDate, sx: { marginTop: 2 }, onClick: e => {
114
114
  e.preventDefault();
@@ -1,8 +1,10 @@
1
1
  export type IAMTokensProps = {
2
- /** Route to navigate when clicking "New IAM token" button. Defaults to '/new/token'. */
2
+ /** Route to navigate when clicking "New API Key" button. Defaults to '/new/token'. */
3
3
  newTokenRoute?: string;
4
4
  /** Base route for the tokens list (used for edit navigation). Defaults to current relative path. */
5
5
  tokensListRoute?: string;
6
+ /** Whether to display view titles/headings. Defaults to true. */
7
+ showTitle?: boolean;
6
8
  };
7
- export declare const IAMTokens: ({ newTokenRoute, tokensListRoute, }?: IAMTokensProps) => import("react/jsx-runtime").JSX.Element;
9
+ export declare const IAMTokens: ({ newTokenRoute, tokensListRoute, showTitle, }?: IAMTokensProps) => import("react/jsx-runtime").JSX.Element;
8
10
  export default IAMTokens;
@@ -10,7 +10,7 @@ import { Box } from '@datalayer/primer-addons';
10
10
  import { EditIcon } from '@datalayer/icons-react';
11
11
  import { TrashIcon } from '@primer/octicons-react';
12
12
  import { useCache, useNavigate, useToast } from '../../hooks';
13
- const TokensTable = ({ tokensListRoute }) => {
13
+ const TokensTable = ({ tokensListRoute, showTitle = true, }) => {
14
14
  const { useTokens, useDeleteToken } = useCache();
15
15
  const { enqueueToast } = useToast();
16
16
  const getTokensQuery = useTokens();
@@ -46,7 +46,7 @@ const TokensTable = ({ tokensListRoute }) => {
46
46
  onSettled: () => setDeletingToken(null),
47
47
  });
48
48
  };
49
- return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "IAM Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No IAM Tokens found." }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "IAM Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
49
+ return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [showTitle && _jsx(Blankslate.Heading, { children: "API Keys" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No API Keys found." }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { children: [showTitle && (_jsxs(_Fragment, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "API Keys" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." })] })), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
50
50
  {
51
51
  header: 'Type',
52
52
  field: 'variant',
@@ -80,8 +80,8 @@ const TokensTable = ({ tokensListRoute }) => {
80
80
  setDeletingToken(null);
81
81
  }, confirmButtonContent: "Delete", confirmButtonType: "danger", children: ["Are you sure you want to delete the token", ' ', _jsx("strong", { children: deletingToken.name }), "? This action cannot be undone."] }))] }));
82
82
  };
83
- export const IAMTokens = ({ newTokenRoute = '/new/token', tokensListRoute, } = {}) => {
83
+ export const IAMTokens = ({ newTokenRoute = '/new/token', tokensListRoute, showTitle = true, } = {}) => {
84
84
  const navigate = useNavigate();
85
- return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "IAM Tokens" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate(newTokenRoute, e), children: "New IAM token" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, { tokensListRoute: tokensListRoute }) }) })] }));
85
+ return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [showTitle && (_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "API Keys" }) })), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate(newTokenRoute, e), children: "New API Key" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, { tokensListRoute: tokensListRoute, showTitle: showTitle }) }) })] }));
86
86
  };
87
87
  export default IAMTokens;
@@ -1,2 +1,3 @@
1
1
  export * from './iam';
2
2
  export * from './profile';
3
+ export * from './otel';
@@ -4,3 +4,4 @@
4
4
  */
5
5
  export * from './iam';
6
6
  export * from './profile';
7
+ export * from './otel';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * DashboardView – Full observability dashboard wrapping OtelLive.
3
+ */
4
+ import React from 'react';
5
+ export interface DashboardViewProps {
6
+ baseUrl?: string;
7
+ /** WebSocket base URL – passed directly to OtelLive to bypass any Vite proxy. */
8
+ wsBaseUrl?: string;
9
+ token?: string;
10
+ autoRefreshMs?: number;
11
+ defaultSignal?: 'traces' | 'logs' | 'metrics';
12
+ limit?: number;
13
+ /** Callback to receive the signal setter from OtelLive. */
14
+ onSignalRef?: (setter: (s: 'traces' | 'logs' | 'metrics') => void) => void;
15
+ }
16
+ export declare const DashboardView: React.FC<DashboardViewProps>;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@primer/react';
3
+ import { OtelLive } from '../../otel';
4
+ export const DashboardView = ({ baseUrl = '', wsBaseUrl, token, autoRefreshMs = 5000, defaultSignal = 'traces', limit = 200, onSignalRef, }) => (_jsx(Box, { sx: { display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden' }, children: _jsx(OtelLive, { baseUrl: baseUrl, wsBaseUrl: wsBaseUrl, token: token, autoRefreshMs: autoRefreshMs, defaultSignal: defaultSignal, limit: limit, onSignalRef: onSignalRef }) }));
@@ -0,0 +1,12 @@
1
+ /**
2
+ * LogsView – Standalone logs-only view.
3
+ * Renders OtelLive defaulting to the logs signal.
4
+ */
5
+ import React from 'react';
6
+ export interface LogsViewProps {
7
+ baseUrl?: string;
8
+ token?: string;
9
+ autoRefreshMs?: number;
10
+ limit?: number;
11
+ }
12
+ export declare const LogsView: React.FC<LogsViewProps>;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@primer/react';
3
+ import { OtelLive } from '../../otel';
4
+ export const LogsView = ({ baseUrl = '', token, autoRefreshMs = 5000, limit = 200, }) => (_jsx(Box, { sx: { display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden' }, children: _jsx(OtelLive, { baseUrl: baseUrl, token: token, autoRefreshMs: autoRefreshMs, defaultSignal: "logs", limit: limit }) }));
@@ -0,0 +1,12 @@
1
+ /**
2
+ * MetricsView – Standalone metrics-only view.
3
+ * Renders OtelLive defaulting to the metrics signal.
4
+ */
5
+ import React from 'react';
6
+ export interface MetricsViewProps {
7
+ baseUrl?: string;
8
+ token?: string;
9
+ autoRefreshMs?: number;
10
+ limit?: number;
11
+ }
12
+ export declare const MetricsView: React.FC<MetricsViewProps>;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@primer/react';
3
+ import { OtelLive } from '../../otel';
4
+ export const MetricsView = ({ baseUrl = '', token, autoRefreshMs = 5000, limit = 200, }) => (_jsx(Box, { sx: { display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden' }, children: _jsx(OtelLive, { baseUrl: baseUrl, token: token, autoRefreshMs: autoRefreshMs, defaultSignal: "metrics", limit: limit }) }));
@@ -0,0 +1,33 @@
1
+ /**
2
+ * OtelHeader – Top-level header bar with branding, signal generator
3
+ * buttons, and a result dialog for OTEL observability applications.
4
+ *
5
+ * This is a generic, reusable header. Authentication state is decoupled:
6
+ * pass `onSignOut` to wire up your own logout action.
7
+ */
8
+ import React from 'react';
9
+ export interface OtelHeaderProps {
10
+ /** Base URL for the generate API (e.g. '/api' or 'https://…'). Defaults to ''. */
11
+ baseUrl?: string;
12
+ /** JWT bearer token for authenticated requests. */
13
+ token?: string;
14
+ /** Optional extra controls rendered to the right of the generate buttons. */
15
+ trailing?: React.ReactNode;
16
+ /**
17
+ * Called before a generate request fires so a parent can navigate to
18
+ * the relevant signal view (e.g. 'logs', 'metrics').
19
+ */
20
+ onNavigate?: (signal: 'traces' | 'logs' | 'metrics') => void;
21
+ /**
22
+ * Optional sign-out handler. When provided a "Sign out" button is shown.
23
+ * Wire this to your auth store's clearAuth / signOut action.
24
+ */
25
+ onSignOut?: () => void;
26
+ /**
27
+ * Whether to show the "+ Traces / + Logs / + Metrics" generate buttons.
28
+ * Defaults to `true`. Pass `false` to hide them (e.g. in agent-runtime views
29
+ * where data is generated by agents, not manually).
30
+ */
31
+ showGenerateButtons?: boolean;
32
+ }
33
+ export declare const OtelHeader: React.FC<OtelHeaderProps>;
@@ -0,0 +1,105 @@
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
+ * OtelHeader – Top-level header bar with branding, signal generator
8
+ * buttons, and a result dialog for OTEL observability applications.
9
+ *
10
+ * This is a generic, reusable header. Authentication state is decoupled:
11
+ * pass `onSignOut` to wire up your own logout action.
12
+ */
13
+ import { useState, useCallback, useRef } from 'react';
14
+ import { Box, Text, Button, Dialog } from '@primer/react';
15
+ import { TelescopeIcon, SignOutIcon } from '@primer/octicons-react';
16
+ import { UserBadge } from '../profile';
17
+ export const OtelHeader = ({ baseUrl = '', token, trailing, onNavigate, onSignOut, showGenerateButtons = true, }) => {
18
+ const [dialogOpen, setDialogOpen] = useState(false);
19
+ const [dialogTitle, setDialogTitle] = useState('');
20
+ const [dialogRequest, setDialogRequest] = useState('');
21
+ const [dialogBody, setDialogBody] = useState('');
22
+ const [dialogVariant, setDialogVariant] = useState('success');
23
+ const [generating, setGenerating] = useState(false);
24
+ const returnFocusRef = useRef(null);
25
+ const generate = useCallback(async (kind, count) => {
26
+ if (onNavigate) {
27
+ const signalMap = {
28
+ traces: 'traces',
29
+ 'ai-traces': 'traces',
30
+ logs: 'logs',
31
+ metrics: 'metrics',
32
+ };
33
+ onNavigate(signalMap[kind]);
34
+ }
35
+ setGenerating(true);
36
+ const url = `${baseUrl}/api/generate/${kind}?count=${count}`;
37
+ setDialogRequest(`POST ${url}`);
38
+ try {
39
+ const headers = {};
40
+ if (token) {
41
+ headers['Authorization'] = `Bearer ${token}`;
42
+ }
43
+ const resp = await fetch(url, { method: 'POST', headers });
44
+ if (!resp.ok)
45
+ throw new Error(`HTTP ${resp.status}`);
46
+ const data = await resp.json();
47
+ setDialogTitle(`Generated ${kind}`);
48
+ setDialogBody(JSON.stringify(data, null, 2));
49
+ setDialogVariant('success');
50
+ }
51
+ catch (err) {
52
+ setDialogTitle(`Error generating ${kind}`);
53
+ setDialogBody(err instanceof Error ? err.message : String(err));
54
+ setDialogVariant('error');
55
+ }
56
+ finally {
57
+ setGenerating(false);
58
+ setDialogOpen(true);
59
+ }
60
+ }, [baseUrl, token, onNavigate]);
61
+ return (_jsxs(_Fragment, { children: [_jsxs(Box, { as: "header", sx: {
62
+ display: 'flex',
63
+ alignItems: 'center',
64
+ gap: 3,
65
+ px: 3,
66
+ py: 2,
67
+ bg: 'canvas.inset',
68
+ borderBottom: '1px solid',
69
+ borderColor: 'border.default',
70
+ flexShrink: 0,
71
+ // Allow overflow visible so the JWT popover can extend below the header.
72
+ overflow: 'visible',
73
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(TelescopeIcon, { size: 20 }), _jsx(Text, { sx: { fontWeight: 'bold', fontSize: 2 }, children: "Datalayer OTEL" })] }), _jsx(Box, { sx: { flex: 1 } }), showGenerateButtons && (_jsxs(_Fragment, { children: [_jsx(Button, { ref: returnFocusRef, size: "small", variant: "primary", disabled: generating, onClick: () => generate('traces', 3), children: "+ Traces" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('ai-traces', 3), children: "+ AI Traces" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('logs', 10), children: "+ Logs" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('metrics', 5), children: "+ Metrics" })] })), trailing, token && _jsx(UserBadge, { token: token }), onSignOut && (_jsx(Button, { size: "small", variant: "invisible", onClick: onSignOut, sx: { color: 'fg.muted' }, leadingVisual: SignOutIcon, children: "Sign out" }))] }), dialogOpen && (_jsxs(Dialog, { title: dialogTitle, onClose: () => setDialogOpen(false), footerButtons: [
74
+ {
75
+ content: 'Close',
76
+ onClick: () => setDialogOpen(false),
77
+ buttonType: 'default',
78
+ },
79
+ ], children: [_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1, color: 'fg.muted' }, children: "Request" }), _jsx(Box, { as: "pre", sx: {
80
+ fontFamily: 'mono',
81
+ fontSize: 1,
82
+ p: 2,
83
+ mt: 1,
84
+ bg: 'canvas.inset',
85
+ borderRadius: 2,
86
+ overflow: 'auto',
87
+ color: 'fg.default',
88
+ whiteSpace: 'pre-wrap',
89
+ wordBreak: 'break-all',
90
+ border: '1px solid',
91
+ borderColor: 'border.default',
92
+ }, children: dialogRequest })] }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1, color: 'fg.muted' }, children: "Response" }), _jsx(Box, { as: "pre", sx: {
93
+ fontFamily: 'mono',
94
+ fontSize: 1,
95
+ p: 2,
96
+ mt: 1,
97
+ bg: 'canvas.subtle',
98
+ borderRadius: 2,
99
+ overflow: 'auto',
100
+ maxHeight: 300,
101
+ color: dialogVariant === 'error' ? 'danger.fg' : 'fg.default',
102
+ whiteSpace: 'pre-wrap',
103
+ wordBreak: 'break-all',
104
+ }, children: dialogBody })] })] }))] }));
105
+ };