@unblind/react 0.1.0-alpha.16 → 0.1.0-alpha.17

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/providers/UnblindClientProvider.tsx","../src/providers/ScopeProvider.tsx","../src/components/Defaults/index.tsx","../src/providers/UnblindProvider.tsx","../src/hooks/useMetrics.ts","../src/hooks/useTimeseries.ts","../src/hooks/utils.ts","../src/hooks/useUsage.ts","../src/hooks/useLogs.ts","../src/components/Timeseries/index.tsx","../src/components/Chart/index.tsx","../src/types.ts","../src/components/Chart/utils.ts","../src/components/Chart/XAxis.ts","../src/components/Tooltip/TooltipManager.ts","../src/components/Tooltip/index.tsx","../src/components/Tooltip/plugin.tsx","../src/components/Chart/YAxis.ts"],"sourcesContent":["import {\n QueryClient,\n QueryClientConfig,\n QueryClientProvider,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\n\nexport type UnblindClientConfig = {\n /**\n * Base URL that client-side hooks will use.\n * Should point to the host app endpoint that is proxied by middleware.\n * Defaults to `/api/unblind`.\n */\n apiBaseUrl: string;\n\n /**\n * Optional custom fetch implementation (for tests or advanced setups).\n * When not provided, the global `fetch` will be used.\n */\n fetchImpl?: typeof fetch;\n};\n\nconst UnblindClientConfigContext = createContext<\n UnblindClientConfig | undefined\n>(undefined);\n\nexport type UnblindClientProviderProps = {\n children?: ReactNode;\n /**\n * Optional QueryClient instance. If not provided, a new one will be created.\n * Useful if you already have a QueryClientProvider in your app and want to reuse it.\n */\n queryClient?: QueryClient;\n\n /**\n * Optional QueryClientConfig. If not provided, a default one will be used.\n * Useful if you already have a QueryClientProvider in your app and want to reuse it.\n */\n queryClientConfig?: QueryClientConfig | undefined;\n\n /**\n * Optional API base URL override. Defaults to `/api/unblind`.\n */\n apiBaseUrl?: string;\n /**\n * Optional custom fetch implementation (for tests or advanced setups).\n */\n fetchImpl?: typeof fetch;\n};\n\n/**\n * UnblindClientProvider sets up the QueryClientProvider (for React Query)\n * and the Unblind client configuration context.\n *\n * @example\n * ```tsx\n * import { UnblindClientProvider } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindClientProvider apiBaseUrl=\"/api/unblind\">\n * <YourComponents />\n * </UnblindClientProvider>\n * );\n * }\n * ```\n */\nexport function UnblindClientProvider({\n children,\n queryClient: providedQueryClient,\n queryClientConfig: providedQueryClientConfig,\n apiBaseUrl = \"/api/unblind\",\n fetchImpl,\n}: UnblindClientProviderProps) {\n // Create a QueryClient if one wasn't provided\n const queryClient = useMemo(() => {\n if (providedQueryClient) {\n return providedQueryClient;\n }\n\n const defaultQueryConfig = {\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n refetchOnMount: false,\n };\n\n const baseConfig = {\n defaultOptions: {\n queries: defaultQueryConfig,\n },\n };\n\n // Merge with provided config, ensuring our defaults aren't overwritten\n if (providedQueryClientConfig) {\n return new QueryClient({\n ...providedQueryClientConfig,\n defaultOptions: {\n ...providedQueryClientConfig.defaultOptions,\n queries: {\n ...defaultQueryConfig,\n ...providedQueryClientConfig.defaultOptions?.queries,\n },\n },\n });\n }\n\n return new QueryClient(baseConfig);\n }, [providedQueryClient, providedQueryClientConfig]);\n\n const configValue: UnblindClientConfig = useMemo(\n () => ({\n apiBaseUrl,\n fetchImpl,\n }),\n [apiBaseUrl, fetchImpl],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <UnblindClientConfigContext.Provider value={configValue}>\n {children}\n </UnblindClientConfigContext.Provider>\n </QueryClientProvider>\n );\n}\n\n/**\n * Access the Unblind client configuration.\n *\n * @throws Error if called outside of an UnblindClientProvider.\n * This ensures that QueryClientProvider is always available for hooks.\n */\nexport function useUnblindClientConfig(): UnblindClientConfig {\n const ctx = useContext(UnblindClientConfigContext);\n\n if (!ctx) {\n throw new Error(\n \"useUnblindConfig must be used within an UnblindClientProvider. \" +\n \"Please wrap your app or component tree with <UnblindClientProvider>.\",\n );\n }\n\n return ctx;\n}\n\n/**\n * Hook to refresh all timeseries data.\n * Invalidates all queries with the 'unblind' and 'timeseries' keys,\n * causing them to refetch automatically.\n *\n * @example\n * ```tsx\n * function Dashboard() {\n * const refresh = useRefresh();\n * return (\n * <>\n * <button onClick={() => refresh()}>Refresh</button>\n * <Timeseries metrics={[\"cpu\"]} />\n * </>\n * );\n * }\n * ```\n */\nexport function useRefresh(): () => Promise<void> {\n const queryClient = useQueryClient();\n\n return useCallback(async () => {\n await queryClient.refetchQueries({\n queryKey: [\"unblind\", \"timeseries\"],\n });\n }, [queryClient]);\n}\n","import React, { createContext, useContext, useMemo } from \"react\";\nimport type {\n TimeseriesQueryConfig,\n TimeRange,\n Appearance,\n ChartVisualConfig,\n} from \"../types\";\nimport { TooltipProps } from \"../components/Tooltip\";\nimport { Loading, Error, Empty } from \"../components/Defaults\";\n\nexport type ScopeConfig = TimeseriesQueryConfig &\n ChartVisualConfig & {\n /**\n * Optional appearance configuration for all components\n * within this scope.\n *\n * Use this to globally override UI components such as loading,\n * or error states.\n *\n * Local overrides passed directly to a component take\n * precedence over this configuration.\n *\n * @example\n * ```tsx\n * <Scope\n * appearance={{\n * components: {\n * Loading: CustomLoading,\n * Error: CustomError,\n * },\n * }}\n * >\n * <App />\n * </Scope>\n * ```\n */\n appearance?: Appearance;\n };\n\nconst ScopeConfigContext = createContext<ScopeConfig | undefined>(undefined);\n\nexport type ScopeProps = ScopeConfig & {\n children?: React.ReactNode;\n};\n\n/**\n * Scope provides scoped configuration for all Unblind components (charts, logs, etc).\n * This includes default time ranges, intervals, attributes, groupBy, operator, appearance, and colors.\n *\n * When nested, child scopes inherit defaults from parent scopes and can override specific values.\n * This allows you to set global defaults (like appearance) at the top level and override\n * settings (like timeRange) in nested scopes.\n *\n * @example\n * ```tsx\n * import { Scope } from '@unblind/react';\n *\n * function App() {\n * return (\n * <Scope\n * appearance={{ components: { Loading: CustomLoading } }}\n * timeRange=\"24h\"\n * >\n * // Components here use 24h and CustomLoading\n *\n * <Scope timeRange=\"1h\">\n * // Components here use 1h but still inherit CustomLoading\n * </Scope>\n * </Scope>\n * );\n * }\n * ```\n */\nexport function Scope({\n children,\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n appearance,\n tooltip,\n colors,\n fill,\n sortByValues,\n}: ScopeProps) {\n const parentContext = useContext(ScopeConfigContext);\n\n // Components\n const LoadingComponent =\n appearance?.components?.Loading ||\n parentContext?.appearance?.components?.Loading;\n const ErrorComponent =\n appearance?.components?.Error ||\n parentContext?.appearance?.components?.Error;\n const TooltipComponent =\n appearance?.components?.Tooltip ||\n parentContext?.appearance?.components?.Tooltip;\n const EmptyComponent =\n appearance?.components?.Empty ||\n parentContext?.appearance?.components?.Empty;\n\n // Tooltip\n const tooltipLayout = tooltip?.layout || parentContext?.tooltip?.layout;\n const tooltipFormat = tooltip?.format || parentContext?.tooltip?.format;\n const tooltipHideAttributes =\n typeof tooltip?.hideAttributes === \"boolean\"\n ? tooltip?.hideAttributes\n : parentContext?.tooltip?.hideAttributes;\n const tooltipHideAttributeKey =\n typeof tooltip?.hideAttributeKey === \"boolean\"\n ? tooltip?.hideAttributeKey\n : parentContext?.tooltip?.hideAttributeKey;\n const tooltipHideMetric =\n typeof tooltip?.hideMetric === \"boolean\"\n ? tooltip?.hideMetric\n : parentContext?.tooltip?.hideMetric;\n\n const memoizedAppearance = useMemo(() => {\n return {\n components: {\n ...(LoadingComponent && { Loading: LoadingComponent }),\n ...(ErrorComponent && { Error: ErrorComponent }),\n ...(TooltipComponent && { Tooltip: TooltipComponent }),\n ...(EmptyComponent && { Empty: EmptyComponent }),\n },\n };\n }, [LoadingComponent, ErrorComponent, TooltipComponent, EmptyComponent]);\n\n const memoizedTooltip = useMemo(() => {\n return {\n layout: tooltipLayout,\n format: tooltipFormat,\n hideAttributes: tooltipHideAttributes,\n hideAttributeKey: tooltipHideAttributeKey,\n hideMetric: tooltipHideMetric,\n };\n }, [\n tooltipLayout,\n tooltipFormat,\n tooltipHideAttributes,\n tooltipHideAttributeKey,\n tooltipHideMetric,\n ]);\n\n const scopeConfigValue: ScopeConfig = useMemo(\n () => ({\n timeRange: timeRange ?? parentContext?.timeRange,\n startTime: startTime ?? parentContext?.startTime,\n endTime: endTime ?? parentContext?.endTime,\n interval: interval ?? parentContext?.interval,\n attributes: attributes ?? parentContext?.attributes,\n groupBy: groupBy ?? parentContext?.groupBy,\n operator: operator ?? parentContext?.operator,\n colors: colors ?? parentContext?.colors,\n sortByValues:\n typeof sortByValues === \"boolean\"\n ? sortByValues\n : parentContext?.sortByValues,\n fill: typeof fill === \"boolean\" ? fill : parentContext?.fill,\n appearance: memoizedAppearance,\n tooltip: memoizedTooltip,\n }),\n [\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n colors,\n sortByValues,\n fill,\n memoizedAppearance,\n memoizedTooltip,\n parentContext,\n ],\n );\n\n return (\n <ScopeConfigContext.Provider value={scopeConfigValue}>\n {children}\n </ScopeConfigContext.Provider>\n );\n}\n\nconst DEFAULT_TIMERANGE = \"6h\" as const;\n\nexport type UseScopeReturn = TimeseriesQueryConfig &\n ChartVisualConfig & {\n timeRange: TimeRange;\n\n appearance: {\n components: {\n Loading: React.ComponentType;\n Error: React.ComponentType;\n Empty: React.ComponentType;\n Tooltip?: React.ComponentType<TooltipProps>;\n };\n };\n };\n\n/**\n * Hook to access the scoped configuration from Scope.\n * Returns all configuration including time range, attributes,\n * groupBy, operator, colors, and UI components.\n *\n * @example\n * ```tsx\n * function MyChart() {\n * const { timeRange, colors, appearance } = useScope();\n * // Use configuration...\n * }\n * ```\n */\nexport function useScope(): UseScopeReturn {\n const ctx = useContext(ScopeConfigContext);\n\n return useMemo(() => {\n return {\n timeRange: ctx?.timeRange || DEFAULT_TIMERANGE,\n ...ctx,\n appearance: {\n components: {\n Loading: ctx?.appearance?.components?.Loading ?? Loading,\n Error: ctx?.appearance?.components?.Error ?? Error,\n Empty: ctx?.appearance?.components?.Empty ?? Empty,\n Tooltip: ctx?.appearance?.components?.Tooltip,\n },\n },\n };\n }, [ctx]);\n}\n","import \"./Defaults.css\";\n\nexport function Empty() {\n return (\n <div className=\"ub-default\">\n <div className=\"ub-empty-content\">\n <div className=\"ub-empty-icon-wrapper\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"ub-icon\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\"\n />\n </svg>\n </div>\n <p\n className=\"ub-empty-text\"\n data-text=\"No data available for this time range\"\n />\n </div>\n </div>\n );\n}\n\nexport function Error() {\n return <div className=\"ub-error\" />;\n}\n\nexport function Loading() {\n return <div className=\"ub-default\" data-text=\"Loading\" />;\n}\n","import {\n UnblindClientProvider,\n UnblindClientProviderProps,\n} from \"./UnblindClientProvider\";\nimport { Scope, ScopeProps } from \"./ScopeProvider\";\n\n// Re-export hooks\nexport { useUnblindClientConfig, useRefresh } from \"./UnblindClientProvider\";\nexport { useScope } from \"./ScopeProvider\";\n\nexport type UnblindProviderProps = UnblindClientProviderProps &\n ScopeProps & { children?: React.ReactNode };\n\n/**\n * UnblindProvider is required for all Unblind hooks to work.\n * It sets up both the QueryClientProvider (for React Query) and the Unblind configuration context.\n * This is a convenience wrapper around `UnblindClientProvider` and `Scope`.\n *\n * @example\n * ```tsx\n * import { UnblindProvider } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindProvider apiBaseUrl=\"/api/unblind\">\n * <YourComponents />\n * </UnblindProvider>\n * );\n * }\n * ```\n */\nexport function UnblindProvider({\n children,\n queryClient,\n apiBaseUrl,\n fetchImpl,\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n appearance,\n tooltip,\n colors,\n sortByValues,\n}: UnblindProviderProps) {\n return (\n <UnblindClientProvider\n queryClient={queryClient}\n apiBaseUrl={apiBaseUrl}\n fetchImpl={fetchImpl}\n >\n <Scope\n timeRange={timeRange}\n startTime={startTime}\n endTime={endTime}\n interval={interval}\n attributes={attributes}\n groupBy={groupBy}\n operator={operator}\n appearance={appearance}\n tooltip={tooltip}\n colors={colors}\n sortByValues={sortByValues}\n >\n {children}\n </Scope>\n </UnblindClientProvider>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useUnblindClientConfig } from \"../providers/UnblindProvider\";\nimport type { MetricMetadataList } from \"../types\";\n\nexport interface UseMetricsReturn {\n metrics: MetricMetadataList | undefined;\n isLoading: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch the list of available metrics metadata.\n *\n * @returns Object containing the metrics list, loading state, error state, and refetch function.\n */\nexport function useMetrics(): UseMetricsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n\n const query = useQuery<MetricMetadataList>({\n queryKey: [\"unblind\", \"metrics\"],\n queryFn: async () => {\n const res = await fetchImpl(`${apiBaseUrl}/metrics`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n throw new Error(\"Error loading metrics metadata\");\n }\n\n if (res.status === 200) {\n const { data: metrics } = await res.json();\n return metrics;\n } else {\n throw new Error(\"Unexpected status code\");\n }\n },\n });\n\n return {\n metrics: query.data,\n isLoading: query.isLoading,\n hasError: query.isError,\n refetch: query.refetch,\n };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport type {\n MetricMetadata,\n Serie,\n TimeseriesQuery,\n TimeseriesQueryConfig,\n} from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\n/**\n * Response structure from the timeseries API.\n */\ntype TimeseriesResponse = {\n request: SeriesAPIRequest;\n metadata: Record<string, MetricMetadata>;\n series: Array<Serie>;\n times: Array<number>;\n isEmpty?: boolean;\n};\n\n/**\n * Internal API request structure.\n */\ninterface SeriesAPIRequest {\n queries: Array<TimeseriesQuery>;\n startTime: number;\n endTime: number;\n interval?: number;\n}\n\nexport interface UseTimeseriesParams extends Pick<\n TimeseriesQueryConfig,\n \"timeRange\" | \"startTime\" | \"endTime\" | \"interval\"\n> {\n /**\n * Array of queries to execute.\n */\n queries: Array<TimeseriesQuery>;\n}\n\nexport interface UseTimeseriesReturn {\n data: {\n series: Serie[];\n times: number[];\n metadata: Record<string, MetricMetadata>;\n };\n isLoading: boolean;\n isFetching: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch timeseries data for metrics.\n *\n * @param params - Configuration object with queries, and either timeRange or startTime/endTime, plus optional interval.\n * @returns Object containing timeseries data, loading states, and error state.\n */\nexport function useTimeseries({\n queries,\n timeRange,\n startTime,\n endTime,\n interval,\n}: UseTimeseriesParams): UseTimeseriesReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n\n const metricNames = useMemo(\n () => queries.map((x) => x.metrics.join(\",\")).join(\",\"),\n [queries],\n );\n const attributes = useMemo(\n () =>\n queries\n .map((x) => {\n const attrs = x.attributes;\n if (!attrs) return \"\";\n\n const attributeKeys = Object.keys(attrs);\n if (attributeKeys.length === 0) return \"\";\n return attributeKeys\n .map((key: string) => key + \":\" + attrs[key]?.join(\",\"))\n .join(\",\");\n })\n .join(\",\"),\n [queries],\n );\n const operators = useMemo(() => queries.map((x) => x.operator), [queries]);\n const groupBy = useMemo(\n () => queries.map((x) => x.groupBy).join(\", \"),\n [queries],\n );\n\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n // Query all widget metrics concurrently\n const query = useQuery<TimeseriesResponse>({\n queryKey: [\n \"unblind\",\n \"timeseries\",\n metricNames,\n attributes,\n // Include explicit times in queryKey for proper caching\n startTime,\n endTime,\n timeRange,\n interval,\n operators,\n groupBy,\n ],\n queryFn: async () => {\n if (!metricNames) {\n throw new Error(\"Missing required parameters\");\n }\n if (metricNames.length === 0) {\n throw new Error(\"No series provided\");\n }\n\n // Calculate times inside queryFn so they're fresh when explicitly refetched\n const [calculatedStartTime, calculatedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const apiRequestBody: SeriesAPIRequest = {\n queries,\n startTime: calculatedStartTime,\n endTime: calculatedEndTime,\n interval,\n };\n const res = await fetchImpl(`${apiBaseUrl}/tenants/timeseries`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(apiRequestBody),\n });\n if (!res.ok) throw new Error(\"Error fetching metric\");\n const { series, times, metadata } = await res.json();\n if (!series) {\n console.error(\"Series not found\");\n throw new Error(\"Series not found\");\n }\n return { series, times, metadata } as TimeseriesResponse;\n },\n enabled: !!metricNames && hasValidTimeConfig,\n });\n\n const {\n metadata,\n series,\n times,\n }: {\n series: Array<Serie>;\n times: Array<number>;\n metadata: Record<string, MetricMetadata>;\n } = useMemo(() => {\n if (!query.data) return { series: [], times: [], metadata: {} };\n return {\n series: query.data.series,\n times: query.data.times,\n metadata: query.data.metadata,\n };\n }, [query]);\n\n const isLoading = query.isLoading;\n const isFetching = query.isFetching;\n const hasError = query.isError;\n\n return {\n data: { series, times, metadata },\n isLoading,\n isFetching,\n hasError,\n refetch: query.refetch,\n };\n}\n","import { TimeRange } from \"@/types\";\nimport ms from \"ms\";\n\nexport function timeRangeToCalculatedTimestamp(\n timeRange: TimeRange,\n): [number, number] {\n const now = Math.floor(Date.now() / 1000);\n const calculatedStartTime = now - Math.floor(ms(timeRange) / 1000);\n const calculatedEndTime = now;\n return [calculatedStartTime, calculatedEndTime];\n}\n\nexport function deduceTimestamp(\n timeRange?: TimeRange,\n startTime?: number,\n endTime?: number,\n): [number, number] {\n let calculatedStartTime: number;\n let calculatedEndTime: number;\n\n if (typeof startTime === \"number\" && typeof endTime === \"number\") {\n calculatedStartTime = startTime!;\n calculatedEndTime = endTime!;\n } else if (timeRange) {\n const [startTime, endTime] = timeRangeToCalculatedTimestamp(timeRange);\n calculatedStartTime = startTime;\n calculatedEndTime = endTime;\n } else {\n throw new Error(\n \"Either timeRange or both startTime and endTime must be provided\",\n );\n }\n\n return [calculatedStartTime, calculatedEndTime];\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport { TimeConfig } from \"@/types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport interface Usage {\n period: {\n startTime: number;\n endTime: number;\n };\n metrics: {\n units: number;\n };\n logs: {\n bytes: number;\n units: number;\n };\n}\n\ntype UsageResponse = {\n data: Array<Usage>;\n};\n\nexport type UseUsageParams = TimeConfig;\n\nexport interface UseUsageReturn {\n usage: Array<Usage>;\n isLoading: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch usage data.\n *\n * @param params - Configuration object with optional timeRange.\n * @returns Object containing usage data, loading state, and error state.\n */\nexport function useUsage({\n timeRange,\n startTime,\n endTime,\n}: UseUsageParams): UseUsageReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useQuery<Array<Usage>>({\n queryKey: [\"unblind\", \"usage\", timeRange, startTime, endTime],\n queryFn: async () => {\n const [calculatedStartTime, calculatedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const endpoint = `${apiBaseUrl}/tenants/usage`;\n\n const res = await fetchImpl(endpoint, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n startTime: calculatedStartTime,\n endTime: calculatedEndTime,\n }),\n });\n if (!res.ok) throw new Error(\"Error fetching usage\");\n const { data: usage } = (await res.json()) as UsageResponse;\n if (!usage) {\n throw new Error(\"usage not found\");\n }\n\n return usage;\n },\n enabled: hasValidTimeConfig,\n });\n\n const usage: Array<Usage> = useMemo(() => {\n if (!query.data) return [];\n return query.data || [];\n }, [query]);\n\n const isLoading = query.isLoading || query.isRefetching;\n const hasError = query.isError;\n\n return {\n usage,\n isLoading,\n hasError,\n refetch: query.refetch,\n };\n}\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport type {\n AttributeWithValue,\n Log,\n PaginatedResponse,\n TimeConfig,\n} from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport type UseLogsParams = {\n /**\n * Array of filters to apply to the logs query.\n */\n filters: Array<AttributeWithValue>;\n} & TimeConfig;\n\nexport interface UseLogsReturn {\n logs: Array<Log>;\n isLoading: boolean;\n hasError: boolean;\n hasNextPage: boolean;\n fetchNextPage: () => void;\n isFetchingNextPage: boolean;\n refetch: () => void;\n}\n\nexport type PaginatedLogsResponse = PaginatedResponse<Log>;\n\n/**\n * Hook to fetch logs data with infinite scroll pagination.\n *\n * @param params - Configuration object with timeRange and filters.\n * @returns Object containing logs data, loading states, and pagination controls.\n */\nexport function useLogs({\n timeRange,\n filters,\n startTime,\n endTime,\n}: UseLogsParams): UseLogsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useInfiniteQuery<PaginatedLogsResponse>({\n queryKey: [\n \"unblind\",\n \"logs\",\n timeRange,\n filters\n .map((x) => x.name + \":\" + x.value)\n .sort()\n .join(\",\"),\n ],\n queryFn: async ({ pageParam }) => {\n const filtersByName = filters.reduce<Record<string, string[]>>(\n (acc, filter) => {\n if (!acc[filter.name]) {\n acc[filter.name] = [];\n }\n acc[filter.name]!.push(filter.value);\n return acc;\n },\n {},\n );\n\n const {\n body = [],\n severity = [],\n \"service.name\": service = [],\n \"trace.id\": traceId = [],\n \"span.id\": spanId = [],\n ...attributes\n } = filtersByName;\n\n const [deducedStartTime, deducedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const res = await fetchImpl(`${apiBaseUrl}/tenants/logs`, {\n method: \"POST\",\n body: JSON.stringify({\n filter: {\n attributes,\n body,\n severity,\n traceId,\n spanId,\n service,\n },\n startTime: deducedStartTime,\n endTime: deducedEndTime,\n pagination: {\n page: pageParam,\n },\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) throw new Error(\"Error fetching logs\");\n\n const { data, next_page } = await res.json();\n\n if (!data) {\n throw new Error(\"logs not found\");\n }\n\n return { data, next_page } as PaginatedLogsResponse;\n },\n enabled: hasValidTimeConfig,\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => lastPage.next_page,\n });\n\n const logs = useMemo(() => {\n if (!query.data) return [];\n return query.data.pages.flatMap((page) => page.data || []);\n }, [query.data]);\n\n return {\n logs,\n isLoading: query.isLoading,\n hasError: query.isError,\n hasNextPage: query.hasNextPage ?? false,\n fetchNextPage: query.fetchNextPage,\n isFetchingNextPage: query.isFetchingNextPage,\n refetch: query.refetch,\n };\n}\n","import { useTimeseries } from \"../../hooks/useTimeseries\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport type {\n ChartType,\n Appearance,\n TimeseriesQueryConfig,\n Threshold,\n ChartVisualConfig,\n} from \"../../types\";\nimport { useMemo } from \"react\";\nimport { Chart } from \"../Chart\";\n\nexport type TimeseriesProps = Exclude<\n TimeseriesQueryConfig,\n \"startTime\" | \"endTime\" | \"timeRange\"\n> &\n ChartVisualConfig & {\n /**\n * Metric name(s) to display in the chart.\n *\n * @example Single metric\n * ```jsx\n * <Timeseries metrics=\"nodejs.eventloop.p50\" />\n * ```\n *\n * @example\n * Multiple metrics\n * ```jsx\n * <Timeseries metrics={[\"nodejs.eventloop.p50\", \"...\"]} />\n * ```\n */\n metrics: Array<string> | string;\n\n /**\n * Chart type. Defaults to \"line\".\n */\n type?: ChartType;\n\n /**\n * Optional threshold\n */\n thresholds?: Array<Threshold>;\n\n /**\n * Optional min value\n */\n min?: number;\n\n /**\n * Optional max value\n */\n max?: number;\n\n /**\n * Unit of measurement for the value.\n *\n * Supports standard unit identifiers:\n * - Data: 'bytes', 'decbytes', 'bits', 'decbits'\n * - Time: 'ms', 's', 'm', 'h', 'd'\n * - Throughput: 'Bps', 'Mbps', 'Gbps'\n * - Percentage: 'percent', 'percentunit'\n * - And many more standard units\n *\n */\n unit?: string;\n\n /**\n * Optional className for the chart container.\n */\n className?: string;\n\n /**\n * Optional appearance configuration for this chart\n */\n appearance?: Appearance;\n };\n\n/**\n * Displays a time series chart.\n * Uses values from <UnblindProvider> or <Scope>\n * when they are not explicitly provided as props.\n *\n * @example\n * ```tsx\n * <Timeseries\n * metrics=\"host.cpu\"\n * attributes={{ \"host.region\": [\"us-east-2\"] }}\n * groupBy=[\"host.name\"]\n * />\n * ```\n *\n * @example Using `<Scope>`\n * ```tsx\n * <Scope timeRange=\"1h\">\n * <Timeseries metrics=\"host.cpu\" />\n * <Timeseries metrics=\"host.memory\" />\n * </Scope>\n * ```\n */\nexport function Timeseries({\n metrics,\n operator: propOperator,\n attributes: propAttributes,\n groupBy: propGroupBy,\n interval: propInterval,\n type = \"line\",\n className,\n appearance: propAppearance,\n unit: propUnit,\n thresholds: propThresholds,\n min: propMin,\n max: propMax,\n colors: propColors,\n tooltip: propTooltip,\n sortByValues: propSortByValues,\n fill: propFill,\n}: TimeseriesProps) {\n // Get defaults from scope\n const scope = useScope();\n\n // Use props if provided, otherwise fall back to scope values\n // Priority: props > scope > defaults\n const interval = propInterval ?? scope.interval;\n const attributes = propAttributes ?? scope.attributes;\n const groupBy = propGroupBy ?? scope.groupBy;\n const operator = propOperator ?? scope.operator;\n const colors = propColors ?? scope.colors;\n const sortByValues =\n typeof propSortByValues === \"boolean\"\n ? propSortByValues\n : scope.sortByValues;\n const fill = typeof propFill === \"boolean\" ? propFill : scope.fill;\n const tooltipComponent =\n propAppearance?.components?.Tooltip ?? scope.appearance.components.Tooltip;\n\n const tooltip = useMemo(() => {\n return {\n layout: propTooltip?.layout ?? scope.tooltip?.layout,\n format: propTooltip?.format ?? scope.tooltip?.format,\n hideAttributes:\n propTooltip?.hideAttributes ?? scope.tooltip?.hideAttributes,\n hideAttributeKey:\n propTooltip?.hideAttributeKey ?? scope.tooltip?.hideAttributeKey,\n hideMetric: propTooltip?.hideMetric ?? scope.tooltip?.hideMetric,\n };\n }, [\n propTooltip?.layout,\n propTooltip?.format,\n propTooltip?.hideAttributes,\n propTooltip?.hideAttributeKey,\n propTooltip?.hideMetric,\n scope.tooltip?.layout,\n scope.tooltip?.format,\n scope.tooltip?.hideAttributes,\n scope.tooltip?.hideAttributeKey,\n scope.tooltip?.hideMetric,\n ]);\n\n const { isLoading, data, hasError } = useTimeseries({\n queries: (Array.isArray(metrics) ? metrics : [metrics]).map((metric) => ({\n metrics: [metric],\n operator,\n attributes,\n groupBy,\n })),\n timeRange: scope.timeRange,\n startTime: scope.startTime,\n endTime: scope.endTime,\n interval,\n });\n\n const { series, times, metadata } = data;\n const isEmpty = series.every((x) => x.isEmpty);\n\n const containerClassName = `ub-chart-container${className ? ` ${className}` : \"\"}`;\n\n if (isLoading) {\n const LoadingComponent =\n propAppearance?.components?.Loading ??\n scope.appearance.components.Loading;\n return (\n <div className={containerClassName}>\n <LoadingComponent />\n </div>\n );\n }\n\n if (isEmpty) {\n const EmptyComponent =\n propAppearance?.components?.Empty ?? scope.appearance.components.Empty;\n\n return (\n <div className={containerClassName}>\n <EmptyComponent />\n </div>\n );\n }\n\n if (hasError) {\n const ErrorComponent =\n propAppearance?.components?.Error ?? scope.appearance.components.Error;\n return (\n <div className={containerClassName}>\n <ErrorComponent />\n </div>\n );\n }\n\n return (\n <Chart\n times={times}\n series={series}\n metadata={metadata}\n type={type}\n className={className}\n tooltip={tooltip}\n colors={colors}\n sortByValues={sortByValues}\n unit={propUnit}\n fill={fill}\n tooltipComponent={tooltipComponent}\n thresholds={propThresholds}\n min={propMin}\n max={propMax}\n />\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport uPlot from \"uplot\";\nimport {\n type MetricMetadata,\n type Serie,\n type ChartVisualConfig,\n type ChartType,\n type Threshold,\n isLineThreshold,\n isRangeThreshold,\n} from \"../../types\";\nimport { TooltipProps } from \"../Tooltip\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport { stack } from \"./YAxis\";\nimport { compareAndResolveUnit, createChartOptions } from \"./utils\";\n\nexport interface ChartProps extends ChartVisualConfig {\n times: Array<number>;\n series: Serie[];\n metadata: Record<string, MetricMetadata>;\n type: ChartType;\n className?: string;\n timeZone?: string;\n options?: uPlot.Options;\n unit?: string;\n tooltipComponent?: React.ComponentType<TooltipProps>;\n thresholds?: Array<Threshold>;\n min?: number;\n max?: number;\n}\n\n/**\n * Renders a chart for time series data\n */\nexport function Chart(props: ChartProps) {\n const {\n times,\n series,\n metadata,\n type,\n className,\n timeZone,\n options: propsOptions,\n tooltip,\n colors: propsColors,\n tooltipComponent,\n sortByValues = false,\n unit: propsUnit,\n fill = false,\n thresholds,\n min: predefinedMin,\n max: predefinedMax,\n } = props;\n const {\n layout: tooltipLayout,\n format: tooltipFormat,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n } = tooltip || {};\n const chartRef = useRef<HTMLDivElement>(null);\n const scope = useScope();\n const colors = propsColors || scope.colors;\n\n useEffect(() => {\n if (!series || series.length === 0) {\n console.warn(\"No series provided\");\n return;\n }\n\n // uPlot aligned data works in the following way:\n // - First array of values is for X-axis\n // - The rest arrays are values for Y-axis\n const data: uPlot.AlignedData = [times];\n let unit: string | undefined = propsUnit;\n series.forEach((serie) => {\n const metricMetadata = metadata[serie.metric];\n\n if (!propsUnit) {\n unit = compareAndResolveUnit(unit, metricMetadata);\n }\n\n data.push(serie.values);\n });\n\n if (thresholds) {\n thresholds.forEach((threshold) => {\n if (isLineThreshold(threshold)) {\n data.push(new Array(times.length).fill(threshold.value));\n } else if (isRangeThreshold(threshold)) {\n data.push(new Array(times.length).fill(threshold.from));\n data.push(new Array(times.length).fill(threshold.to));\n }\n });\n }\n\n const stacked = type === \"bar\" || type === \"area\";\n const stackedData = stack(data, !stacked);\n const container = chartRef.current;\n let u: uPlot | null = null;\n\n if (container) {\n const opts = createChartOptions(\n container,\n metadata,\n stackedData,\n unit,\n series,\n type,\n stacked,\n colors,\n fill,\n sortByValues,\n timeZone,\n tooltipComponent,\n false,\n tooltipFormat,\n tooltipLayout,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n thresholds,\n predefinedMin,\n predefinedMax,\n );\n\n u = new uPlot({ ...opts, ...propsOptions }, stackedData.data, container);\n const resizeObserver = new ResizeObserver(() => {\n u?.setSize({\n width: container.clientWidth,\n height: container.clientHeight,\n });\n });\n\n resizeObserver.observe(container);\n\n return () => {\n u?.destroy();\n resizeObserver.disconnect();\n };\n }\n }, [\n series,\n times,\n type,\n metadata,\n timeZone,\n tooltipComponent,\n colors,\n fill,\n sortByValues,\n propsUnit,\n tooltipFormat,\n tooltipLayout,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n thresholds,\n predefinedMin,\n predefinedMax,\n propsOptions,\n ]);\n\n return (\n <div\n ref={chartRef}\n className={\"ub-chart-container\" + (className ? ` ${className}` : \"\")}\n />\n );\n}\n","/**\n * Shared types for Unblind React library.\n */\n\nimport { StringValue } from \"ms\";\nimport {\n TooltipFormat,\n TooltipLayout,\n TooltipProps,\n} from \"./components/Tooltip\";\n\nexport type MetricType =\n | \"gauge\"\n | \"sum\"\n | \"histogram\"\n | \"summary\"\n | \"exphistogram\";\n\n/**\n * Aggregation operators available for metric queries.\n */\nexport type AggregationOperator =\n | \"avg\"\n | \"sum\"\n | \"max\"\n | \"min\"\n | \"p90\"\n | \"p99\"\n | \"p50\";\n\n/**\n * Attribute filter with a specific value.\n */\nexport interface AttributeWithValue {\n name: string;\n value: string;\n}\n\n/**\n * Metric metadata returned by the API.\n */\nexport interface MetricMetadata {\n name: string;\n description: string;\n displayName?: string;\n unit: {\n /// The unique, case-sensitive UCUM identifier (e.g., \"mL\", \"m/s2\")\n code?: string;\n /// The human-readable primary name (e.g., \"milliliter\")\n name?: string;\n /// Alternate names (e.g., \"cubic centimeter\" for \"cm3\")\n synonym?: string;\n /// The physical dimension or property (e.g., \"Volume\", \"Mass\")\n category?: string;\n };\n type: MetricType;\n operator?: AggregationOperator;\n}\n\n/**\n * List of metric metadata.\n */\nexport type MetricMetadataList = Array<MetricMetadata>;\n\n/**\n * A single time series data point.\n */\nexport interface Serie {\n metric: string;\n attributes?: Record<string, string>;\n values: Array<number>;\n queryName?: string;\n queryIndex: number;\n isEmpty?: boolean;\n}\n\n/**\n * Query definition for timeseries requests.\n */\nexport interface TimeseriesQuery {\n queryName?: string;\n metrics: Array<string>;\n groupBy?: Array<string>;\n operator?: AggregationOperator;\n attributes?: Partial<Record<string, Array<string>>>;\n}\n\n/**\n * Chart types available\n */\nexport type ChartType = \"bar\" | \"line\" | \"area\" | \"step\" | \"spline\";\n\n/**\n * Time Range definition:\n *\n * e.g. 5secs, 4h, 3 days, and so on.\n */\nexport type TimeRange = StringValue;\n\nexport interface Log {\n timestamp: number;\n trace_id?: string;\n span_id?: string;\n severity_text: Severity;\n attributes?: Record<string, string>;\n service_name?: string;\n body?: string;\n}\n\n/**\n * Default paginated response structure\n */\nexport type PaginatedResponse<T> = {\n data: Array<T>;\n next_page?: string;\n};\n\n/**\n * Different severity values based on OTEL\n * https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext\n */\nexport type Severity = \"TRACE\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\" | \"FATAL\";\n\nexport type Colors =\n | Array<string>\n | { fill: Array<string>; border: Array<string> }\n | ((\n serie: Serie,\n index: number,\n type: ChartType,\n isFilling?: boolean,\n ) => string);\n\n/**\n * Interval expressed in seconds.\n */\nexport type Interval = number;\n\nexport type TimeConfig = {\n /**\n * Optional time range. If not provided, will use timeRange from the provider.\n * Ignored if startTime and endTime are provided.\n */\n timeRange?: TimeRange;\n /**\n * Optional start time (Unix timestamp in seconds).\n * If provided along with endTime, takes priority over timeRange.\n */\n startTime?: number;\n /**\n * Optional end time (Unix timestamp in seconds).\n * If provided along with startTime, takes priority over timeRange.\n */\n endTime?: number;\n};\n\n/**\n * Base query configuration for timeseries data.\n * Used across components and providers.\n */\nexport type TimeseriesQueryConfig = TimeConfig & {\n /**\n * Optional interval in seconds. If not provided, will use interval from the provider.\n */\n interval?: Interval;\n /**\n * Optional attributes filter. If not provided, will use attributes from the provider.\n */\n attributes?: Record<string, Array<string>>;\n /**\n * Optional groupBy. If not provided, will use groupBy from the provider.\n */\n groupBy?: Array<string>;\n /**\n * Optional aggregation operator. If not provided, will use the operator from the provider.\n */\n operator?: AggregationOperator;\n};\n\n/**\n * Visual/presentation configuration for charts.\n */\nexport interface ChartVisualConfig {\n /**\n * Optional color configuration\n */\n colors?: Colors;\n\n /**\n * Optional labeling order.\n * Defaults to false\n */\n sortByValues?: boolean;\n\n /**\n * Optional to fill charts.\n *\n * Bar and area charts are filled by default.\n */\n fill?: boolean;\n\n /**\n * Optional tooltip configuration.\n */\n tooltip?: TooltipConfig;\n}\n\nexport interface TooltipConfig {\n layout?: TooltipLayout;\n format?: TooltipFormat;\n hideAttributeKey?: boolean;\n hideAttributes?: boolean;\n hideMetric?: boolean;\n}\n\n/**\n * Appearance configuration for Unblind components.\n *\n * Allows consumers to override internal UI components used for\n * loading, empty, and error states.\n *\n * Overrides can be provided globally via Scope or\n * locally per component.\n *\n * This API customizes presentation only; component state and data\n * fetching logic remain internal.\n */\nexport type Appearance = {\n components?: {\n Loading?: React.ComponentType;\n Error?: React.ComponentType;\n Empty?: React.ComponentType;\n Tooltip?: React.ComponentType<TooltipProps>;\n };\n};\n\n/**\n * Typeguard for LineThreshold\n */\nexport const isLineThreshold = (\n threshold: Threshold,\n): threshold is LineThreshold => {\n return \"value\" in threshold;\n};\n\n/**\n * Typeguard for RangeThreshold\n */\nexport const isRangeThreshold = (\n threshold: Threshold,\n): threshold is RangeThreshold => {\n return \"from\" in threshold && \"to\" in threshold;\n};\n\nexport type ThresholdLevel = \"info\" | \"warning\" | \"error\" | \"ok\";\nexport type ThresholdLineType = \"line\" | \"dashed\" | \"bold\";\nexport type LineThreshold = {\n value: number;\n type?: ThresholdLineType;\n level?: ThresholdLevel;\n label?: string;\n};\n\nexport type RangeThreshold = {\n from: number;\n to?: number;\n type?: ThresholdLineType;\n level?: ThresholdLevel;\n label?: string;\n};\nexport type Threshold = LineThreshold | RangeThreshold;\n","import {\n ChartType,\n Colors,\n isLineThreshold,\n isRangeThreshold,\n MetricMetadata,\n Serie,\n Threshold,\n ThresholdLevel,\n ThresholdLineType,\n} from \"@/types\";\n\nimport uPlot, { Band } from \"uplot\";\nimport { getValueFormat } from \"@unblind/units\";\nimport { createXAxisConfig } from \"./XAxis\";\nimport { buildScaleRange, createYAxisConfig } from \"./YAxis\";\nimport { TooltipLayout, TooltipFormat, TooltipProps } from \"../Tooltip\";\nimport { tooltipPlugin } from \"../Tooltip/plugin\";\n\n/**\n * Default chart color palette.\n *\n * Note:\n * Colors are cycled in order when the number of series exceeds\n * the palette length.\n */\nexport const DEFAULT_COLORS: string[] = [\n \"oklch(0.70 0.24 293)\",\n \"oklch(0.85 0.18 95)\",\n \"oklch(0.65 0.25 255)\",\n \"oklch(0.72 0.26 27)\",\n \"oklch(0.70 0.22 150)\",\n \"oklch(0.78 0.18 50)\",\n \"oklch(0.72 0.18 215)\",\n \"oklch(0.70 0.27 301)\",\n \"oklch(0.75 0.20 90)\",\n \"oklch(0.68 0.23 277)\",\n \"oklch(0.70 0.18 193)\",\n \"oklch(0.68 0.25 4)\",\n];\n\n/**\n * Gets the chart font from CSS variables\n *\n * We want to use the same font style the application is using.\n * Some sort of manual font heritage.\n */\nexport const getChartFont = (fontFamiliy: string) => {\n const style = getComputedStyle(document.documentElement);\n const fontStyle = `${style.getPropertyValue(\"--ub-chart-font-size\").trim()} ${fontFamiliy}`;\n\n return fontStyle;\n};\n\nexport const compareAndResolveUnit = (\n paramsUnit?: string,\n metricMetadata?: MetricMetadata,\n) => {\n let unit = paramsUnit;\n\n if (\n metricMetadata &&\n metricMetadata.unit &&\n metricMetadata.unit.code &&\n metricMetadata.unit.code !== \"1\"\n ) {\n if (!unit && metricMetadata.unit) {\n unit = metricMetadata.unit.code;\n } else if (unit && metricMetadata.unit.code !== unit) {\n unit = undefined;\n }\n }\n\n return unit;\n};\n\n// Type guards for colors\nexport function isColorsObject(\n colors: Colors,\n): colors is { fill: string[]; border: string[] } {\n return (\n typeof colors === \"object\" &&\n colors !== null &&\n !Array.isArray(colors) &&\n Array.isArray(\n (colors as { fill: Array<string>; border: Array<string> }).fill,\n ) &&\n Array.isArray(colors.border)\n );\n}\n\nexport function isColorsArray(colors: Colors): colors is string[] {\n return Array.isArray(colors);\n}\n\nexport function isColorsFunction(\n colors: Colors,\n): colors is (\n serie: Serie,\n index: number,\n type: ChartType,\n isFilling?: boolean,\n) => string {\n return typeof colors === \"function\";\n}\n\n// Util to get a color from the colros variable.\nexport const getColor = (\n serie: Serie,\n index: number,\n colors: Colors | undefined,\n type: ChartType,\n computedStyle: CSSStyleDeclaration,\n isFilling?: boolean,\n) => {\n if (colors) {\n // User provided\n if (isColorsArray(colors)) {\n return colors[index];\n } else if (isColorsObject(colors)) {\n return isFilling ? colors.fill[index] : colors.border[index];\n } else {\n return colors(serie, index, type, isFilling);\n }\n }\n\n // If no colors provided, try to get from CSS variables\n const cssColorVar = isFilling\n ? `--ub-chart-serie-color-fill-${index + 1}`\n : `--ub-chart-serie-color-${index + 1}`;\n\n const cssColor = computedStyle.getPropertyValue(cssColorVar).trim();\n\n if (cssColor) {\n return cssColor;\n }\n\n // Static fallback\n return isFilling && (type === \"step\" || type === \"line\" || type === \"spline\")\n ? `oklch(from ${DEFAULT_COLORS[index % DEFAULT_COLORS.length]} l c h / 0.4)`\n : DEFAULT_COLORS[index % DEFAULT_COLORS.length];\n};\n\n// ============================================================================\n// Chart Path & Fill Generation\n// ============================================================================\n\n/**\n * Generates the path for a particular chart type\n */\nconst generatePath = (type: \"bar\" | \"line\" | \"area\" | \"step\" | \"spline\") => {\n const barsPath = uPlot.paths.bars!({ size: [0.6, 100], radius: 0, gap: 0 });\n const linearPath = uPlot.paths.linear!({\n alignGaps: 0,\n });\n const splinePath = uPlot.paths.spline!({ alignGaps: 1 });\n const steppedPath = uPlot.paths.stepped!({ alignGaps: 1 });\n\n switch (type) {\n case \"line\":\n return linearPath;\n case \"bar\":\n return barsPath;\n case \"area\":\n return linearPath;\n case \"step\":\n return steppedPath;\n case \"spline\":\n return splinePath;\n default:\n return linearPath;\n }\n};\n\n/**\n * Fill color for a particular chart type\n */\nconst generateFill = (\n serie: Serie,\n index: number,\n colors: Colors | undefined,\n type: ChartType,\n computedStyle: CSSStyleDeclaration,\n) => {\n return getColor(serie, index, colors, type, computedStyle, true);\n};\n\n// ============================================================================\n// Chart Utilities\n// ============================================================================\n\nconst getStrokeWidthByType = (type: ChartType, fill: boolean): number => {\n switch (type) {\n case \"bar\":\n return 1;\n case \"line\":\n return fill ? 1.5 : 2;\n case \"spline\":\n return fill ? 1.5 : 2;\n case \"area\":\n return 1;\n case \"step\":\n return 1.5;\n default:\n return 1;\n }\n};\n\nconst cursorMovement: uPlot.Cursor.MousePosRefiner = (u, left, top) => {\n if (left < 0 || top < 0) {\n // Return early if it is out of bounds\n return [left, top];\n }\n\n const xVal = u.posToVal(left, \"x\");\n\n const xData = u.data[0];\n if (!xData || xData.length === 0) {\n return [left, top];\n }\n\n let nearestIdx = 0;\n\n let lo = 0;\n let hi = xData.length - 1;\n\n while (hi - lo > 1) {\n const mid = Math.floor((lo + hi) / 2);\n const midVal = xData[mid];\n if (midVal != null && midVal < xVal) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const loVal = xData[lo];\n const hiVal = xData[hi];\n if (loVal != null && hiVal != null) {\n nearestIdx = Math.abs(loVal - xVal) < Math.abs(hiVal - xVal) ? lo : hi;\n } else if (loVal != null) {\n nearestIdx = lo;\n } else if (hiVal != null) {\n nearestIdx = hi;\n }\n\n // Search backward from nearestIdx to find first non-null value\n let snapIdx = nearestIdx;\n for (let i = nearestIdx; i >= 0; i--) {\n let hasValue = false;\n\n for (let j = 1; j < u.data.length; j++) {\n const seriesData = u.data[j];\n if (seriesData && seriesData[i] != null) {\n hasValue = true;\n break;\n }\n }\n if (hasValue) {\n snapIdx = i;\n break;\n }\n }\n\n const snapVal = xData[snapIdx];\n if (snapVal == null) {\n return [left, top];\n }\n\n const snappedLeft = u.valToPos(snapVal, \"x\");\n\n return [snappedLeft, top];\n};\n\nconst mutateAxisGridAndTextColor = (\n opts: uPlot.Options,\n computedStyle: CSSStyleDeclaration,\n) => {\n const textColor = computedStyle\n .getPropertyValue(\"--ub-chart-font-color\")\n .trim();\n const gridColor = computedStyle\n .getPropertyValue(\"--ub-chart-grid-color\")\n .trim();\n\n const xAxis = opts.axes?.[0];\n const yAxis = opts.axes?.[1];\n if (xAxis) {\n xAxis.stroke = textColor;\n if (xAxis.grid) {\n xAxis.grid.stroke = gridColor;\n } else {\n xAxis.grid = {\n stroke: gridColor,\n };\n }\n }\n\n if (yAxis) {\n yAxis.stroke = textColor;\n if (yAxis.grid) {\n yAxis.grid.stroke = gridColor;\n } else {\n yAxis.grid = {\n stroke: gridColor,\n };\n }\n }\n\n return opts;\n};\n\n// ============================================================================\n// Thresholds\n// ============================================================================\n\nconst getThresholdColor = (\n computedStyle: CSSStyleDeclaration,\n level?: ThresholdLevel,\n): string => {\n return (\n computedStyle.getPropertyValue(`--ub-chart-threshold-${level}`).trim() ||\n computedStyle.getPropertyValue(\"--ub-chart-threshold-default\").trim()\n );\n};\n\nconst getThresholdRangeFillColor = (\n computedStyle: CSSStyleDeclaration,\n level?: ThresholdLevel,\n): string => {\n return (\n computedStyle\n .getPropertyValue(`--ub-chart-threshold-${level}-fill`)\n .trim() ||\n computedStyle.getPropertyValue(\"--ub-chart-threshold-default-fill\").trim()\n );\n};\n\nconst getThresholdWidth = (lineType?: ThresholdLineType): number => {\n switch (lineType) {\n case \"bold\":\n return 2;\n case \"line\":\n return 1;\n case \"dashed\":\n return 1;\n default:\n return 1;\n }\n};\n\nconst getThresholdDash = (\n lineType?: ThresholdLineType,\n): number[] | undefined => {\n return lineType === \"line\" ? undefined : [5, 5];\n};\n\nconst createThresholds = (\n computedStyle: CSSStyleDeclaration,\n thresholds?: Array<Threshold>,\n): Array<uPlot.Series> => {\n if (!thresholds) {\n return [];\n } else {\n const thresholdSeries: Array<uPlot.Series> = [];\n\n thresholds.forEach((threshold) => {\n const thresholdSerie = {\n label: threshold.label,\n stroke: getThresholdColor(computedStyle, threshold.level),\n width: getThresholdWidth(),\n dash: getThresholdDash(threshold.type),\n points: { show: false, size: 0 },\n spanGaps: true,\n show: true,\n auto: false,\n };\n\n if (isLineThreshold(threshold)) {\n thresholdSeries.push(thresholdSerie);\n } else {\n thresholdSeries.push(thresholdSerie);\n thresholdSeries.push(thresholdSerie);\n }\n });\n\n return thresholdSeries;\n }\n};\n\n// ============================================================================\n// Chart Options Creation\n// ============================================================================\nexport const createChartBands = (\n series: Array<Serie>,\n stackedData: {\n data: uPlot.AlignedData;\n bands: Array<Band>;\n },\n computedStyle: CSSStyleDeclaration,\n thresholds?: Array<Threshold>,\n) => {\n if (!thresholds || !thresholds.some((x) => isRangeThreshold(x))) {\n return stackedData.bands;\n }\n\n const rangeThresholdBands = thresholds\n .map((threshold, idx) => {\n if (!isRangeThreshold(threshold)) {\n return null;\n }\n\n return {\n series: [series.length + idx * 2 + 1, series.length + idx * 2 + 2],\n fill: getThresholdRangeFillColor(computedStyle, threshold.level),\n dir: 1,\n };\n })\n .filter((band) => band !== null);\n\n return [...stackedData.bands, ...rangeThresholdBands];\n};\n\nconst createChartSeries = (\n series: Array<Serie>,\n type: ChartType,\n colors: Colors | undefined,\n fill: boolean,\n stacked: boolean,\n computedStyle: CSSStyleDeclaration,\n thresholds?: Array<Threshold>,\n): Array<uPlot.Series> => {\n const strokeWidth = getStrokeWidthByType(type, fill);\n return [\n {},\n ...series.map((serie, index) => ({\n label: serie.metric,\n stroke: getColor(serie, index, colors, type, computedStyle),\n width: strokeWidth,\n points: { show: false },\n spanGaps: true,\n paths: generatePath(type),\n fill:\n fill || stacked\n ? generateFill(serie, index, colors, type, computedStyle)\n : undefined,\n })),\n ...createThresholds(computedStyle, thresholds),\n ];\n};\n\nconst parseUnit = (unit?: string) => {\n if (typeof unit === \"string\") {\n const sUnit = String(unit).toLowerCase().trim();\n if (sUnit === \"by\") {\n return \"bytes\";\n } else if (sUnit.startsWith(\"{\") && sUnit.endsWith(\"}\")) {\n // Very common in OTEL for {cpu} or {memory} units\n return \"short\";\n }\n }\n\n return unit;\n};\n\n/**\n * Creates the complete uPlot options object for the chart\n */\nexport const createChartOptions = (\n container: HTMLDivElement,\n metadata: Record<string, MetricMetadata>,\n stackedData: {\n data: uPlot.AlignedData;\n bands: Array<uPlot.Band>;\n },\n unit: string | undefined,\n series: Array<Serie>,\n type: ChartType,\n stacked: boolean,\n colors: Colors | undefined,\n fill: boolean,\n sortByValues: boolean,\n timeZone?: string,\n tooltipComponent?: React.ComponentType<TooltipProps>,\n relativeTimeAxis?: boolean,\n format?: TooltipFormat,\n layout?: TooltipLayout,\n hideAttributeKey?: boolean,\n hideAttributes?: boolean,\n hideMetric?: boolean,\n thresholds?: Array<Threshold>,\n predefinedMin?: number,\n predefinedMax?: number,\n): uPlot.Options => {\n const computedStyle = window.getComputedStyle(container);\n const fontFamily = computedStyle.fontFamily;\n\n const initialWidth = container?.clientWidth ?? 1050;\n const initialHeight = container?.clientHeight ?? 250;\n const formatUnit = parseUnit(unit);\n const formatter = getValueFormat(formatUnit === \"1\" ? null : formatUnit);\n const formattingFunction = (v: number) => {\n const f = formatter(v, Number.isInteger(v) ? 0 : 2);\n return f.text + (f.suffix?.trim() || \"\");\n };\n\n const opts: uPlot.Options = {\n width: initialWidth,\n height: initialHeight,\n scales: {\n y: {\n range: buildScaleRange(unit, predefinedMin, predefinedMax),\n },\n },\n plugins: [\n tooltipPlugin(\n formattingFunction,\n stacked,\n metadata,\n sortByValues,\n timeZone,\n tooltipComponent,\n series,\n layout,\n format,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n ),\n ],\n padding: relativeTimeAxis ? [10, 15, 10, 15] : [8, 15, 8, 15],\n cursor: {\n y: false,\n sync: { key: \"_\" },\n drag: {\n setScale: true,\n x: true,\n y: false,\n },\n move: cursorMovement,\n },\n series: createChartSeries(\n series,\n type,\n colors,\n fill,\n stacked,\n computedStyle,\n thresholds,\n ),\n bands: createChartBands(series, stackedData, computedStyle, thresholds) as\n | uPlot.Band[]\n | undefined,\n axes: [\n createXAxisConfig(fontFamily, timeZone, relativeTimeAxis),\n createYAxisConfig(formatter, fontFamily),\n ],\n legend: {\n show: false,\n },\n };\n\n return mutateAxisGridAndTextColor(opts, computedStyle);\n};\n","import {\n dateTimeFormat,\n dateTimeFormatTimeAgo,\n systemDateFormats,\n} from \"@unblind/units\";\nimport { getChartFont } from \"./utils\";\n\n// Time unit sizes in milliseconds\nconst timeUnitSize = {\n millisecond: 1,\n second: 1000,\n minute: 60000,\n hour: 3600000,\n day: 86400000,\n month: 2419200000, // 28 days\n year: 31536000000,\n};\n\n// Time axis increments (in seconds)\nconst timeIncrs = {\n second: [1, 2, 5, 10, 15, 30],\n minute: [1, 2, 5, 10, 15, 30],\n hour: [1, 2, 3, 4, 6, 8, 12],\n day: [1, 2, 3, 7, 14],\n month: [1, 2, 3, 6],\n year: [1, 2, 5, 10, 20, 50, 100],\n};\n\nfunction formatTime(\n splits: number[],\n foundIncr: number,\n range: number,\n timeZone?: string,\n): string[] {\n // Handle Month/Year increments\n if (foundIncr > 7 * timeUnitSize.day) {\n let format = systemDateFormats.interval.year;\n\n const yearRoundedToDay =\n Math.round(timeUnitSize.year / timeUnitSize.day) * timeUnitSize.day;\n const incrementRoundedToDay =\n Math.round(foundIncr / timeUnitSize.day) * timeUnitSize.day;\n\n if (incrementRoundedToDay === yearRoundedToDay) {\n format = systemDateFormats.interval.year;\n } else if (foundIncr <= timeUnitSize.year) {\n format = systemDateFormats.interval.month;\n } else {\n format = systemDateFormats.interval.day;\n }\n\n return splits.map((v) => dateTimeFormat(v, { format, timeZone }));\n }\n\n // Handle Intraday (Hours, Minutes, Seconds)\n return splits.map((v) => {\n const date = new Date(v);\n\n const showSeconds = foundIncr < timeUnitSize.minute;\n const showMillis = foundIncr < timeUnitSize.second;\n\n // Check for Midnight in the specific TimeZone\n // We use \"en-GB\" here strictly to check the \"00:00\" pattern reliably\n // without worrying about AM/PM locale differences in the logic.\n const checkTimeStr = date.toLocaleTimeString(\"en-GB\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n timeZone,\n });\n\n // IF it is midnight (and we aren't zooming into seconds), SHOW DATE\n if (\n (checkTimeStr === \"00:00\" || checkTimeStr === \"24:00\") &&\n !showSeconds &&\n !showMillis\n ) {\n return date.toLocaleDateString(undefined, {\n day: \"2-digit\",\n month: \"short\",\n timeZone,\n });\n }\n\n return date.toLocaleTimeString(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: showSeconds ? \"2-digit\" : undefined,\n fractionalSecondDigits: showMillis ? 3 : undefined,\n hour12: false,\n timeZone,\n });\n });\n}\n\n/**\n * X-Axis: Tick interval selection.\n * Picks the best increment size based on target spacing\n */\nfunction findBestIncrement(targetSeconds: number): {\n increment: number;\n multiplier: number;\n} {\n const scales = [\n { size: timeUnitSize.second / 1000, increments: timeIncrs.second },\n { size: timeUnitSize.minute / 1000, increments: timeIncrs.minute },\n { size: timeUnitSize.hour / 1000, increments: timeIncrs.hour },\n { size: timeUnitSize.day / 1000, increments: timeIncrs.day },\n { size: timeUnitSize.month / 1000, increments: timeIncrs.month },\n { size: timeUnitSize.year / 1000, increments: timeIncrs.year },\n ];\n\n for (const scale of scales) {\n for (const mult of scale.increments) {\n const incr = scale.size * mult;\n if (incr >= targetSeconds) {\n return { increment: incr * 1000, multiplier: mult };\n }\n }\n }\n\n // Fallback to largest year increment\n const lastScale = scales[scales.length - 1] as (typeof scales)[0];\n const lastMult = lastScale.increments[\n lastScale.increments.length - 1\n ] as number;\n return {\n increment: lastScale.size * lastMult * 1000,\n multiplier: lastMult,\n };\n}\n\n/**\n * X-Axis: Generates splits using the x latest timestamp and soonest timestamp.\n */\nfunction generateRelativeXAxisSplits(\n u: uPlot,\n min: number,\n max: number,\n): number[] {\n return [min, max];\n}\n\n/**\n * X-Axis: Generate splits (tick positions) for the time axis\n */\nfunction generateXAxisSplits(\n u: uPlot,\n min: number,\n max: number,\n timeZone?: string,\n): number[] {\n const range = max - min;\n const px = u.width;\n const approxTicks = Math.floor(px / 100);\n const targetSeconds = range / approxTicks;\n const { increment, multiplier } = findBestIncrement(targetSeconds);\n const step = increment / 1000;\n const splits: number[] = [];\n\n // For very short ranges (< 12 hours)\n if (range < 12 * 3600) {\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n }\n\n // For 12h-3days ranges\n if (range < 3 * 86400) {\n // For 6h+ increments, align to nice hour boundaries\n if (increment >= 6 * 3600 * 1000) {\n const hourStep = increment / 1000 / 3600; // e.g., 6, 12, etc.\n const startDate = new Date(min * 1000);\n\n if (timeZone === \"UTC\") {\n const currentHour = startDate.getUTCHours();\n const alignedHour = Math.floor(currentHour / hourStep) * hourStep;\n startDate.setUTCHours(alignedHour, 0, 0, 0);\n } else {\n const currentHour = startDate.getHours();\n const alignedHour = Math.floor(currentHour / hourStep) * hourStep;\n startDate.setHours(alignedHour, 0, 0, 0);\n }\n\n let t = startDate.getTime() / 1000;\n if (t < min) {\n t += step;\n }\n\n while (t <= max) {\n splits.push(t);\n t += step;\n }\n return splits;\n }\n\n // For smaller increments in 12h-3day ranges\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n }\n\n // For ranges >= 3 days, align to midnight\n if (increment >= timeUnitSize.day) {\n const currentDate = new Date(min * 1000);\n\n if (timeZone === \"UTC\") {\n currentDate.setUTCHours(0, 0, 0, 0);\n if (currentDate.getTime() / 1000 < min) {\n currentDate.setUTCDate(currentDate.getUTCDate() + multiplier);\n }\n } else {\n currentDate.setHours(0, 0, 0, 0);\n if (currentDate.getTime() / 1000 < min) {\n currentDate.setDate(currentDate.getDate() + multiplier);\n }\n }\n\n let t = currentDate.getTime() / 1000;\n while (t <= max) {\n splits.push(t);\n if (timeZone === \"UTC\") {\n currentDate.setUTCDate(currentDate.getUTCDate() + multiplier);\n } else {\n currentDate.setDate(currentDate.getDate() + multiplier);\n }\n t = currentDate.getTime() / 1000;\n }\n return splits;\n }\n\n // Default: simple rounding\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n}\n\n/**\n * X-Axis: Format splits for relative time axis (only show labels at first and last)\n */\nfunction generateRelativeXAxisValues(\n u: uPlot,\n splits: number[],\n timeZone?: string,\n): string[] {\n if (splits.length === 0) return [];\n\n return splits.map((timestamp, index) => {\n // Only show labels for first and last positions\n if (index === 0 || index === splits.length - 1) {\n // Convert seconds to milliseconds for dateTimeFormatTimeAgo\n return dateTimeFormatTimeAgo(timestamp * 1000, { timeZone });\n }\n return \"\"; // Return empty string for all other positions\n });\n}\n\n/**\n * X-Axis: Format splits into display labels\n */\nfunction generateXAxisValues(\n u: uPlot,\n splits: number[],\n timeZone?: string,\n): string[] {\n const scale = u.scales.x;\n const range = ((scale?.max ?? 0) - (scale?.min ?? 0)) * 1000; // Convert to ms\n const approxTicks = Math.floor(u.width / 100);\n const targetSeconds = range / 1000 / approxTicks;\n const { increment } = findBestIncrement(targetSeconds);\n\n // Convert splits from seconds to milliseconds for formatTime\n const splitsInMs = splits.map((s) => s * 1000);\n return formatTime(splitsInMs, increment, range, timeZone);\n}\n\n/**\n * X-Axis: Create complete axis configuration\n */\nexport function createXAxisConfig(\n fontFamily: string,\n timeZone?: string,\n relativeTimeAxis: boolean = false,\n): uPlot.Axis {\n const splits: uPlot.Axis.Splits = relativeTimeAxis\n ? (u, _, min, max) => generateRelativeXAxisSplits(u, min, max)\n : (u, _, min, max) => generateXAxisSplits(u, min, max, timeZone);\n const values: uPlot.Axis.Values = relativeTimeAxis\n ? (u, splits) => generateRelativeXAxisValues(u, splits, timeZone)\n : (u, splits) => generateXAxisValues(u, splits, timeZone);\n\n return {\n font: getChartFont(fontFamily),\n labelFont: getChartFont(fontFamily),\n grid: {\n show: false,\n width: 0.5,\n },\n ticks: {\n width: 0.5,\n },\n splits,\n values,\n size: 20,\n };\n}\n","import { computePosition, flip, offset } from \"@floating-ui/dom\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport uPlot from \"uplot\";\n\n// Constants\nconst TOOLTIP_DISTANCE_CURSOR = 4;\nconst TOOLTIP_PADDING_FROM_CURSOR = 8;\n\nexport interface AnchorPosition {\n left?: number;\n top?: number;\n}\n\n/**\n * We want to make sure we have a single\n * Tooltip in the whole application.\n *\n * The tooltip manager is in charge of the tooltip lifecycle.\n */\nclass TooltipManager {\n private overlay: HTMLElement | null = null;\n private reactRoot: Root | null = null;\n private renderedUplot: uPlot | null = null;\n\n initialize() {\n if (this.overlay) return;\n\n this.overlay = document.createElement(\"div\");\n this.overlay.id = \"unblind-tooltip-overlay\";\n this.overlay.style.display = \"none\";\n this.overlay.style.position = \"fixed\";\n this.overlay.style.pointerEvents = \"none\";\n this.overlay.style.zIndex = \"9999\";\n document.body.appendChild(this.overlay);\n\n this.reactRoot = createRoot(this.overlay);\n }\n\n getOverlay(): HTMLElement | null {\n return this.overlay;\n }\n\n render(u: uPlot | null, content: React.ReactElement | null) {\n if (this.reactRoot) {\n this.reactRoot.render(content);\n this.renderedUplot = u;\n } else {\n this.renderedUplot = null;\n }\n }\n\n show() {\n if (this.overlay) {\n this.overlay.style.display = \"block\";\n }\n }\n\n hide(u: uPlot) {\n if (u !== this.renderedUplot) {\n console.warn(\"Hide call plot\");\n return;\n }\n if (this.overlay) {\n this.overlay.style.display = \"none\";\n }\n this.render(null, null);\n }\n\n getRenderedUplot(): uPlot | null {\n return this.renderedUplot;\n }\n\n /**\n * Positions the tooltip overlay relative to the cursor\n * @param anchor - Cursor position\n */\n async positionTooltip(anchor: AnchorPosition): Promise<void> {\n const overlay = this.getOverlay();\n if (overlay) {\n const { x, y } = await computePosition(\n {\n getBoundingClientRect: () => ({\n x: anchor.left!,\n y: anchor.top!,\n width: 0,\n height: 0,\n top: anchor.top!,\n left: anchor.left!,\n right: anchor.left!,\n bottom: anchor.top!,\n }),\n },\n overlay,\n {\n placement: \"top-start\",\n strategy: \"fixed\" as const,\n middleware: [\n offset({\n mainAxis: TOOLTIP_DISTANCE_CURSOR,\n crossAxis: TOOLTIP_PADDING_FROM_CURSOR,\n }),\n flip(),\n ],\n },\n );\n\n overlay.style.left = `${x}px`;\n overlay.style.top = `${y}px`;\n }\n }\n}\n\n// Singleton instance\nexport const tooltipManager = new TooltipManager();\n","import { dateTimeFormat } from \"@unblind/units\";\nimport { useMemo } from \"react\";\nimport \"./Tooltip.css\";\nimport { MetricMetadata } from \"@/types\";\nimport uPlot from \"uplot\";\n\n// Types\nexport interface TooltipItem {\n metric: MetricMetadata;\n serie: uPlot.Series;\n color: string;\n value?: number;\n formattedValue?: string;\n attributes?: Record<string, string>;\n}\n\nexport type TooltipLayout = \"auto\" | \"group-by-metric\" | \"flat\";\n/**\n * Formats for rendering attribute and metric names.\n * @example\n * nodejs.eventloop.p50\n * ```\n * original: \"nodejs.eventloop.p50\"\n * suffix: \"p50\"\n * title: \"Nodejs Eventloop P50\"\n * ```\n */\nexport type TooltipFormat = \"original\" | \"suffix\" | \"title\";\nexport interface TooltipProps {\n timestamp: number;\n items: TooltipItem[];\n timeZone?: string;\n}\n\ninterface TooltipExtendedProps extends TooltipProps {\n spansMultipleDays?: boolean;\n sortByValues: boolean;\n stacked: boolean;\n hideAttributeKey?: boolean;\n hideMetric?: boolean;\n hideAttributes?: boolean;\n layout?: TooltipLayout;\n format?: TooltipFormat;\n}\n\ninterface GroupedItems {\n label: string;\n items: TooltipItem[];\n}\n\n// Helper Functions\nfunction getMetricName(item: TooltipItem): string {\n return (\n item.metric.displayName ||\n item.metric.name ||\n (typeof item.serie.label === \"string\"\n ? item.serie.label\n : item.serie.label?.textContent || \"Unknown\")\n );\n}\n\nfunction getSortKey(item: TooltipItem): string {\n const attrs = item.attributes || {};\n if (attrs[\"service.name\"]) return String(attrs[\"service.name\"]);\n\n const keys = Object.keys(attrs);\n const firstKey = keys[0];\n return firstKey ? String(attrs[firstKey]) : \"z-fallback\";\n}\n\nfunction sortItemsByValue(items: TooltipItem[]): TooltipItem[] {\n return [...items].sort(\n (a, b) => (Number(b.value) || 0) - (Number(a.value) || 0),\n );\n}\n\nfunction sortItemsAlphabetically(items: TooltipItem[]): TooltipItem[] {\n return [...items].sort((a, b) => getSortKey(a).localeCompare(getSortKey(b)));\n}\n\nfunction groupItemsByMetric(\n items: TooltipItem[],\n): Record<string, TooltipItem[]> {\n const groups: Record<string, TooltipItem[]> = {};\n\n items.forEach((item) => {\n const metricName = getMetricName(item);\n if (!groups[metricName]) {\n groups[metricName] = [];\n }\n groups[metricName].push(item);\n });\n\n return groups;\n}\n\nfunction createGroupedItems(\n items: TooltipItem[],\n sortByValues: boolean,\n layout: TooltipLayout,\n format: TooltipFormat = \"original\",\n): GroupedItems[] {\n // For flat layout, everything in one group\n if (layout === \"flat\") {\n const sortedItems = sortByValues\n ? sortItemsByValue(items)\n : sortItemsAlphabetically(items);\n\n return [{ label: \"\", items: sortedItems }];\n }\n\n // Group-by-metric layout\n const groups = groupItemsByMetric(items);\n const sortedGroupKeys = Object.keys(groups).sort((a, b) =>\n a.localeCompare(b),\n );\n\n return sortedGroupKeys.map((key) => {\n const groupItems = groups[key];\n if (!groupItems) return { label: formatLabel(key, format), items: [] };\n\n const sortedGroupItems = sortByValues\n ? sortItemsByValue(groupItems)\n : sortItemsAlphabetically(groupItems);\n\n return { label: formatLabel(key, format), items: sortedGroupItems };\n });\n}\n\nfunction formatLabel(\n label: string,\n format: TooltipFormat,\n skipUppercase?: boolean,\n): string {\n switch (format) {\n case \"suffix\": {\n // container.cpu.time -> Time | time (if skipUppercase)\n const parts = label.split(/[._-]/);\n const last = parts[parts.length - 1] || \"\";\n\n if (skipUppercase) return last;\n\n return last.charAt(0).toUpperCase() + last.slice(1).toLowerCase();\n }\n\n case \"title\":\n // container.cpu.time -> Container Cpu Time\n // or: container cpu time (if skipUppercase)\n return label\n .split(/[._-]/)\n .map((word) =>\n skipUppercase\n ? word\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n )\n .join(\" \");\n\n case \"original\":\n default:\n return label;\n }\n}\n\n/**\n * Picks the tooltip layout.\n *\n * - Returns an explicit layout if provided (except \"auto\").\n * - When using \"auto\", groups by metric if items have attributes.\n * - Otherwise, defaults to a flat layout.\n */\nfunction determineLayout(\n items: TooltipItem[],\n explicitLayout?: TooltipLayout,\n): TooltipLayout {\n if (explicitLayout && explicitLayout !== \"auto\") {\n return explicitLayout;\n }\n\n // Auto-detect logic\n const hasAttributes = items.some(\n (item) => item.attributes && Object.keys(item.attributes).length > 0,\n );\n const uniqueMetrics = new Set(items.map(getMetricName));\n if (hasAttributes && uniqueMetrics.size >= 1) {\n return \"group-by-metric\";\n }\n\n return \"flat\";\n}\n\nfunction TooltipItemRowAttributes({\n hideAttributeKey,\n attribute,\n format,\n isLastAttribute,\n}: {\n hideAttributeKey: boolean;\n isLastAttribute: boolean;\n attribute: [string, string];\n format: TooltipFormat;\n}) {\n const [key, value] = attribute;\n\n return (\n <div className=\"ub-tooltip-item-attribute-container\">\n <div\n className={\n \"ub-truncate\" +\n (hideAttributeKey\n ? \" ub-tooltip-item-hidden-attribute-key\"\n : \" ub-tooltip-item-attribute-with-key\")\n }\n >\n {!hideAttributeKey && (\n <span className=\"ub-tooltip-item-attribute-key\">\n {formatLabel(key, format)}\n {\":\"}\n </span>\n )}\n <span className=\"ub-tooltip-item-attribute-value ub-truncate\">\n {value}\n </span>\n </div>\n {!isLastAttribute && hideAttributeKey && (\n <span\n data-text=\", \"\n className=\"ub-tooltip-item-attribute-divider\"\n ></span>\n )}\n </div>\n );\n}\n\n/**\n * Has the two possible following structures:\n *\n * 1. {{metricName}} {{value}}\n * 2. {{metricName}} {{attributes}} {{value}}\n * 3. {{attributes}} {{value}}\n *\n **/\nfunction TooltipItemRow({\n item,\n hideMetric,\n hideAttributeKey,\n hideAttributes,\n format,\n}: {\n item: TooltipItem;\n hideMetric: boolean;\n hideAttributeKey: boolean;\n hideAttributes: boolean;\n format: TooltipFormat;\n}) {\n const entries = Object.entries(item.attributes || {});\n const attributes: [string, string][] = entries;\n const showAttributes = attributes.length > 0 && !hideAttributes;\n\n if (hideAttributes && hideMetric) {\n console.warn(\"Invalid configuration. Attributes and metrics are hidden.\");\n }\n\n return (\n <div className=\"ub-tooltip-item-row\">\n <div\n className={\n \"ub-tooltip-item-left\" +\n (hideMetric ? \" ub-tooltip-item-hidden-metric\" : \"\")\n }\n >\n <div className=\"ub-tooltip-item-heading\">\n <span\n className=\"ub-tooltip-item-dot\"\n style={{ backgroundColor: item.color }}\n />\n {!hideMetric ? (\n <span\n className={\n \"ub-truncate\" +\n (showAttributes\n ? \" ub-tooltip-item-metric-name-label\"\n : \" ub-tooltip-item-metric-name\")\n }\n >\n {formatLabel(item.metric.name, format)}\n </span>\n ) : (\n <div className=\"ub-tooltip-item-attributes\">\n {attributes.map((attribute, i) => (\n <TooltipItemRowAttributes\n key={\"attr_\" + attribute[0]}\n hideAttributeKey={hideAttributeKey}\n attribute={attribute}\n format={format}\n isLastAttribute={i >= attributes.length - 1}\n />\n ))}\n </div>\n )}\n </div>\n {showAttributes && !hideMetric && (\n <div className=\"ub-tooltip-item-attributes\">\n {attributes.map((attribute, i) => (\n <TooltipItemRowAttributes\n key={\"attr_\" + attribute[0]}\n hideAttributeKey={hideAttributeKey}\n attribute={attribute}\n format={format}\n isLastAttribute={i >= attributes.length - 1}\n />\n ))}\n </div>\n )}\n </div>\n\n <div className=\"ub-tooltip-item-value ub-truncate\">\n {item.formattedValue ?? (\n <span className=\"ub-tooltip-item-value--empty\">—</span>\n )}\n </div>\n </div>\n );\n}\n\nfunction Divider({\n className = \"ub-tooltip-divider\",\n ...props\n}: React.ComponentPropsWithoutRef<\"hr\">) {\n return <hr role=\"presentation\" {...props} className={className} />;\n}\n\n// Tooltip Group Component\nfunction TooltipGroup({\n group,\n groupIdx,\n hideAttributeKey,\n hideMetric,\n hideAttributes,\n format,\n hideGroupLabel,\n}: {\n group: GroupedItems;\n groupIdx: number;\n hideAttributeKey: boolean;\n hideAttributes: boolean;\n hideMetric: boolean;\n hideGroupLabel: boolean;\n format: TooltipFormat;\n}) {\n return (\n <div key={group.label} className=\"ub-tooltip-group\">\n {!hideGroupLabel && (\n <div\n className={`ub-tooltip-group-header ub-truncate${\n groupIdx > 0 ? \" ub-tooltip-group-header--separator\" : \"\"\n }`}\n >\n {group.label}\n </div>\n )}\n\n <div className=\"ub-tooltip-items\">\n {group.items.map((item, idx) => (\n <TooltipItemRow\n key={`item-${groupIdx}-${idx}`}\n item={item}\n format={format}\n hideAttributeKey={hideAttributeKey}\n hideMetric={hideMetric}\n hideAttributes={hideAttributes}\n />\n ))}\n </div>\n </div>\n );\n}\n\n// Tooltip Component\n//\n// Tooltip scenarios are a bit complex,\n// but we have the following variables:\n//\n// 1. A single metric with no attributes\n// 2. A single metric and multiple attribute sets\n// 3. A single metric and a single attribute set\n// 4. Multiple metrics\n// 5. Multiple metrics and a single attribute set\n// 6. Multiple metrics and multiple attribute sets\n//\n// Also:\n// a. Displaying attributes is optional\n// b. Displaying attributes keys is optional\n// c. If attributes are present, and there is only one metric,\n// displaying metric name is optional\n//\n// Possible layouts:\n// a. Flat\n// b. Groupped by attributes or metrics.\n//\n// So we end up with the following scenarios:\n//\n// Ref. {{attributes}} = [{{attribute.key}}: {{attribute.value}}, ..{{...}}]\n//\n// 1. A single metric with no attributes (metric hidding is not possible):\n// a. {{metricName}} {{value}}\n//\n// 2. A single metric and multiple attributes:\n// a. {{metricName}} {{attributes}} {{value}}\n// b. {{attributes}} {{value}}\n// c. {{metricName}} {{value}}\n// d. {{metricName}} (groupped)\n// {{attributes}} {{value}}\n// e. {{attributes}} (groupped)\n// {{metricName}} {{value}}\n//\n// 3. A single metric and a single attribute:\n// a. {{metricName}} {{attribute}} {{value}}\n// b. {{attribute}} {{value}}\n// c. {{metricName}} {{value}}\n//\n// 4. Multiple metrics (metric hidding is not possible):\n// a. {{metricName}} {{value}}\n//\n// 5. Multiple metrics and a single attribute\n// a. {{metricName}} {{attribute}} {{value}}\n// {{metricName}} {{attribute}} {{value}}\n// b. {{attribute}} {{value}} (not possible)\n// c. {{metricName}} {{value}}\n// {{metricName}} {{value}}\n// d. {{attribute}} (groupped)\n// {{metricName}} {{value}}\n//\n// 6. Multiple metrics and multiple attributes\n// a. {{metricName}} {{attributes}} {{value}}\n// {{metricName}} {{attributes}} {{value}}\n// b. {{metricName}} {{value}} (hide attributes)\n// {{metricName}} {{value}}\n// c. {{metricName}} (groupped)\n// {{attributes}} {{value}}\n// {{metricName}} (groupped)\n// {{attributes}} {{value}}\n// d. {{attributes}} (groupped)\n// {{metricName}} {{value}}\n// {{attributes}} (groupped)\n// {{metricName}} {{value}}\n// e. {{attributes}} {{value}} (not possible)\n//\n// The following scenarios shouldn't be possible:\n// a. One serie with attributes, and another with none.\n// b. Empty or null attributes or metric names.\n// c. Duplicated metric + attribute set combination.\nexport function Tooltip({\n timestamp,\n items,\n timeZone,\n spansMultipleDays,\n sortByValues,\n format = \"original\",\n layout: propsLayout = \"flat\",\n hideAttributeKey: propsHideAttributeKey,\n hideMetric: propsHideMetric,\n hideAttributes: propsHideAttributes,\n}: TooltipExtendedProps) {\n const formattedTime = spansMultipleDays\n ? dateTimeFormat(timestamp * 1000, {\n format: \"MMM DD, HH:mm\",\n timeZone,\n })\n : dateTimeFormat(timestamp * 1000, {\n format: \"HH:mm\",\n timeZone,\n });\n\n const layout = useMemo(\n () => determineLayout(items, propsLayout),\n [items, propsLayout],\n );\n const hideGroupLabel = layout === \"flat\";\n const isGroupByMetric = layout === \"group-by-metric\";\n\n // Logic: Hide metric name in row if explicit prop says so,\n // OR if we are already grouping by metric (redundant),\n // OR if there is only 1 metric total (redundant).\n const shouldHideMetric =\n typeof propsHideMetric === \"boolean\" ? propsHideMetric : isGroupByMetric;\n const hideAttributeKey =\n typeof propsHideAttributeKey === \"boolean\" ? propsHideAttributeKey : false;\n const hideAttributes =\n typeof propsHideAttributes === \"boolean\" ? propsHideAttributes : false;\n\n const groupedItems = useMemo(() => {\n return createGroupedItems(items, sortByValues, layout, format);\n }, [items, sortByValues, layout, format]);\n\n return (\n <div className=\"ub-tooltip\">\n <div className=\"ub-tooltip-datetime\">{formattedTime}</div>\n\n <Divider />\n\n {groupedItems.length > 0 ? (\n <div className=\"ub-tooltip-content\">\n {groupedItems.map((group, groupIdx) => (\n <TooltipGroup\n key={group.label}\n group={group}\n groupIdx={groupIdx}\n hideAttributeKey={hideAttributeKey}\n hideMetric={shouldHideMetric}\n hideAttributes={hideAttributes}\n hideGroupLabel={hideGroupLabel}\n format={format}\n />\n ))}\n </div>\n ) : (\n <div className=\"ub-tooltip-no-data\">No data available</div>\n )}\n </div>\n );\n}\n","import uPlot from \"uplot\";\nimport type { MetricMetadata, Serie } from \"../../types\";\nimport { tooltipManager } from \"./TooltipManager\";\nimport {\n Tooltip,\n TooltipFormat,\n TooltipItem,\n TooltipLayout,\n TooltipProps,\n} from \".\";\n\n/**\n * Checks if the data spans multiple calendar days\n * @param xData - Array of timestamps\n * @param timeZone - Timezone string (e.g., \"UTC\", \"America/New_York\")\n * @returns True if data spans multiple days\n */\nfunction checkSpansMultipleDays(\n xData: uPlot.TypedArray | number[],\n timeZone?: string,\n): boolean {\n if (!xData || xData.length === 0) return false;\n\n const minTimestamp = xData[0];\n const maxTimestamp = xData[xData.length - 1];\n\n if (minTimestamp == null || maxTimestamp == null) return false;\n\n const minDate = new Date(minTimestamp * 1000);\n const maxDate = new Date(maxTimestamp * 1000);\n\n const getDateString = (date: Date) => {\n if (timeZone === \"UTC\") {\n return `${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()}`;\n }\n return date.toLocaleDateString(undefined, { timeZone });\n };\n\n return getDateString(minDate) !== getDateString(maxDate);\n}\n\n/**\n * Finds the nearest data point with a non-null value\n * @param u - uPlot instance\n * @param idx - Current cursor index\n * @returns Index of nearest non-null data point, or the original index\n */\nfunction findNearestNonNullIndex(u: uPlot, idx: number): number {\n let hasNonNullValue = false;\n\n // Check if current index has any non-null values\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[idx] != null) {\n hasNonNullValue = true;\n break;\n }\n }\n\n if (hasNonNullValue) return idx;\n\n // Search for nearest non-null value\n const dataLength = u.data[0].length;\n\n for (\n let distance = 1;\n idx + distance < dataLength || idx - distance >= 0;\n distance++\n ) {\n const leftCandidate = idx - distance;\n const rightCandidate = idx + distance;\n\n if (leftCandidate >= 0) {\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[leftCandidate] != null) {\n return leftCandidate;\n }\n }\n }\n\n if (rightCandidate < dataLength) {\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[rightCandidate] != null) {\n return rightCandidate;\n }\n }\n }\n }\n\n return idx;\n}\n\n/**\n * Builds tooltip items from uPlot data\n * @param u - uPlot instance\n * @param actualIdx - Data index to extract values from\n * @param formatValue - Function to format numeric values\n * @param stacked - Whether the chart is stacked\n * @param series - Original series configuration\n * @returns Array of tooltip items and hasAttributes flag\n */\nfunction buildTooltipItems(\n u: uPlot,\n actualIdx: number,\n formatValue: (v: number) => string,\n stacked: boolean,\n metadata: Record<string, MetricMetadata>,\n series?: Serie[],\n): { items: TooltipItem[]; hasAttributes: boolean } {\n let hasAttributes = false;\n const items: TooltipItem[] = [];\n\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n let rawVal = (u.data[seriesIndex]?.[actualIdx] ?? null) as number | null;\n\n if (stacked && rawVal != null && seriesIndex > 1) {\n const prevVal = u.data[seriesIndex - 1]?.[actualIdx] ?? 0;\n rawVal = rawVal - prevVal;\n }\n\n const s = u.series[seriesIndex] as uPlot.Series;\n const originalSerie = series?.[seriesIndex - 1];\n if (!originalSerie) {\n console.warn(\"Original serie not found\");\n continue;\n }\n const metric = metadata[originalSerie.metric];\n if (!metric) {\n console.warn(\"Metric metadata not found\");\n continue;\n }\n\n const stroke = s?.stroke;\n const color: string =\n typeof stroke === \"function\"\n ? (stroke(u, seriesIndex) as string)\n : ((stroke as string) ?? \"#ffffff00\");\n const formattedValue =\n rawVal == null\n ? undefined\n : formatValue\n ? formatValue(rawVal)\n : String(rawVal);\n\n if (\n originalSerie?.attributes &&\n Object.keys(originalSerie?.attributes).length > 0\n ) {\n hasAttributes = true;\n }\n\n items.push({\n metric,\n color,\n value: rawVal === null ? undefined : rawVal,\n formattedValue,\n attributes: originalSerie?.attributes,\n serie: s,\n });\n }\n\n return { items, hasAttributes };\n}\n\n/**\n * uPlot plugin for interactive tooltips\n * Extracted from: https://github.com/leeoniya/uPlot/blob/master/demos/cursor-tooltip.html\n */\nexport function tooltipPlugin(\n formatValue: (v: number) => string,\n stacked: boolean,\n metadata: Record<string, MetricMetadata>,\n sortByValues: boolean,\n timeZone?: string,\n appearance?: React.ComponentType<TooltipProps>,\n series?: Serie[],\n layout?: TooltipLayout,\n format?: TooltipFormat,\n hideAttributeKey?: boolean,\n hideAttributes?: boolean,\n hideMetric?: boolean,\n) {\n let over: HTMLElement;\n let boundingLeft: number;\n let boundingTop: number;\n let isHovering = false;\n\n function syncBounds(): void {\n const bbox = over.getBoundingClientRect();\n boundingLeft = bbox.left;\n boundingTop = bbox.top;\n }\n\n return {\n hooks: {\n init: (u: uPlot) => {\n tooltipManager.initialize();\n over = u.over;\n\n window.addEventListener(\"scroll\", syncBounds, true);\n window.addEventListener(\"resize\", syncBounds);\n\n over.onmouseenter = () => {\n isHovering = true;\n tooltipManager.show();\n };\n\n over.onmouseleave = () => {\n isHovering = false;\n\n if (tooltipManager.getRenderedUplot() === u) {\n tooltipManager.hide(u);\n }\n };\n\n syncBounds();\n },\n\n setSize: () => {\n syncBounds();\n },\n\n setCursor: (u: uPlot) => {\n const { left, top, idx } = u.cursor;\n const shouldHideTooltip = !isHovering || idx == null;\n\n if (shouldHideTooltip) {\n if (tooltipManager.getRenderedUplot() === u) {\n tooltipManager.hide(u);\n }\n return;\n }\n\n const xData = u.data[0];\n const spansMultipleDays = checkSpansMultipleDays(xData, timeZone);\n const actualIdx = findNearestNonNullIndex(u, idx);\n const timestamp = u.data[0][actualIdx];\n\n if (timestamp === undefined) {\n return;\n }\n\n const { items } = buildTooltipItems(\n u,\n actualIdx,\n formatValue,\n stacked,\n metadata,\n series,\n );\n\n tooltipManager.show();\n\n const AppearanceTooltip = appearance;\n const tooltipElement = AppearanceTooltip ? (\n <AppearanceTooltip\n timestamp={timestamp}\n items={items}\n timeZone={timeZone}\n />\n ) : (\n <Tooltip\n timestamp={timestamp}\n items={items}\n timeZone={timeZone}\n spansMultipleDays={spansMultipleDays}\n stacked={stacked}\n sortByValues={sortByValues}\n layout={layout}\n format={format}\n hideAttributeKey={hideAttributeKey}\n hideAttributes={hideAttributes}\n hideMetric={hideMetric}\n />\n );\n\n tooltipManager.render(u, tooltipElement);\n tooltipManager.positionTooltip({\n left: (left || 0) + boundingLeft,\n top: (top || 0) + boundingTop,\n });\n },\n\n destroy(u: uPlot) {\n window.removeEventListener(\"scroll\", syncBounds, true);\n window.removeEventListener(\"resize\", syncBounds);\n\n if (over) {\n over.onmouseenter = null;\n over.onmouseleave = null;\n }\n\n if (tooltipManager.getRenderedUplot() === u) {\n tooltipManager.hide(u);\n }\n },\n },\n };\n}\n","import uPlot from \"uplot\";\nimport { getChartFont } from \"./utils\";\n\nexport const calculateNiceStep = (maxMin: number, maxTicks: number = 4) => {\n // Determine step size based on min/max value (targeting ~4-5 ticks)\n const roughStep = maxMin / maxTicks;\n\n // Round to \"nice\" numbers (1, 2, 5) at any magnitude\n const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));\n const residual = roughStep / magnitude;\n\n let niceStep;\n if (residual > 5) {\n niceStep = 10 * magnitude;\n } else if (residual > 2) {\n niceStep = 5 * magnitude;\n } else if (residual > 1) {\n niceStep = 2 * magnitude;\n } else {\n niceStep = magnitude;\n }\n const niceVal = Math.ceil(maxMin / niceStep) * niceStep;\n\n return niceVal;\n};\n\nconst usePredefinedIfExists = (\n predefinedNumber: number | undefined,\n defaultNumber: number,\n) => {\n return typeof predefinedNumber === \"number\"\n ? predefinedNumber\n : defaultNumber;\n};\n\nexport const buildScaleRange: (\n unit: string | undefined,\n predefinedMin?: number | undefined,\n predefinedMax?: number | undefined,\n) => uPlot.Scale.Range = (\n unit: string | undefined,\n predefinedMin?: number | undefined,\n predefinedMax?: number | undefined,\n) => {\n return (_: uPlot, dataMin: number, dataMax: number) => {\n if (dataMin === 0 && dataMax === 0) {\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, 100),\n ];\n }\n // Add a bit of padding at the top\n const padding = 1;\n const max = dataMax * (1 + padding);\n const min = dataMin < 0 ? dataMin * (1 + padding) : dataMin * (1 - padding);\n\n if (unit === \"percent\" || unit === \"percentunit\") {\n if (dataMax > 100) {\n if (dataMin < 0) {\n return [\n usePredefinedIfExists(predefinedMin, min),\n usePredefinedIfExists(predefinedMax, max),\n ];\n }\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, max),\n ];\n } else if (dataMin < 0) {\n return [\n usePredefinedIfExists(predefinedMin, min),\n usePredefinedIfExists(predefinedMax, 100),\n ];\n }\n\n // Normal case\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, 100),\n ];\n }\n\n // For edge case where max is 0 or very close to 0\n if (dataMax <= 0.9999) {\n if (dataMin <= 0) {\n return [\n usePredefinedIfExists(predefinedMin, min),\n usePredefinedIfExists(predefinedMax, 1),\n ];\n }\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, 1),\n ];\n }\n\n const niceMax = calculateNiceStep(max);\n const niceMin = calculateNiceStep(Math.abs(min));\n\n // Important to not do <= 0, otherwise will break some edge cases where dataMin = 0\n if (dataMin < 0) {\n return [\n usePredefinedIfExists(predefinedMax, -niceMin),\n usePredefinedIfExists(predefinedMax, niceMax),\n ];\n }\n\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, niceMax),\n ];\n };\n};\n\n/**\n * Y-Axis: Calculate axis size based on longest value label\n */\nconst calculateYAxisSize = (\n self: uPlot,\n values: Array<string>,\n axisIdx: number,\n cycleNum: number,\n): number => {\n const axis = self.axes[axisIdx];\n\n // bail out, force convergence\n if (cycleNum > 1)\n // Do not use .size, it doesn't work. Use ._size instead.\n return (axis as { _size: number })?._size || 0;\n\n let axisSize = (axis?.ticks?.size || 0) + (axis?.gap || 0);\n\n // find longest value\n const longestVal = (values ?? []).reduce(\n (acc, val) => (val.length > acc.length ? val : acc),\n \"\",\n );\n\n if (longestVal != \"\") {\n self.ctx.font = axis?.font?.[0] ?? self.ctx.font;\n axisSize += self.ctx.measureText(longestVal).width / devicePixelRatio;\n }\n\n return Math.ceil(axisSize);\n};\n\n/**\n * Y-Axis: Create complete axis configuration\n */\nexport function createYAxisConfig(\n formatter: (\n v: number,\n decimals?: number,\n ) => { text: string; suffix?: string },\n fontFamiliy: string,\n): uPlot.Axis {\n return {\n gap: 0,\n font: getChartFont(fontFamiliy),\n labelFont: getChartFont(fontFamiliy),\n grid: {\n show: true,\n width: 0.5,\n },\n ticks: {\n width: 0.5,\n },\n values: (_, vals) =>\n vals.map((v) => {\n const formmatedVal = formatter(v);\n return formmatedVal.text + (formmatedVal.suffix?.trim() || \"\");\n }),\n size: calculateYAxisSize,\n space: 60,\n };\n}\n\n/**\n * Stacking data for bar charts\n * Extracted and mutated from: https://github.com/leeoniya/uPlot/blob/master/demos/stack.js\n */\nexport function stack(\n data: uPlot.AlignedData,\n omit: boolean,\n): {\n data: uPlot.AlignedData;\n bands: Array<uPlot.Band>;\n} {\n const bands: Array<uPlot.Band> = [];\n const xAxis = data[0];\n const xAxisLen = xAxis.length;\n const accum = Array(xAxisLen).fill(0);\n const accumData: uPlot.AlignedData = [xAxis];\n\n data.forEach((serie, i) => {\n // Skip x-axis\n if (i === 0) return;\n if (omit) {\n accumData.push(serie);\n } else {\n accumData.push(\n serie.map(\n (value, index) => (accum[index] = accum[index] + (value || 0)),\n ),\n );\n }\n });\n\n data.forEach((_, serieIndex) => {\n if (serieIndex === 0 || omit) return;\n bands.push({\n series: [data.findIndex((_, j) => j > serieIndex), serieIndex],\n });\n });\n\n return {\n data: accumData,\n bands: bands.filter((b) => b.series[1] > -1),\n };\n}\n"],"mappings":"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","names":["QueryClient","QueryClientProvider","useQueryClient","createContext","useCallback","useContext","useMemo","jsx","UnblindClientConfigContext","UnblindClientProvider","children","providedQueryClient","providedQueryClientConfig","apiBaseUrl","fetchImpl","queryClient","defaultQueryConfig","baseConfig","configValue","useUnblindClientConfig","ctx","useRefresh","createContext","useContext","useMemo","jsx","jsxs","Empty","Error","Loading","jsx","ScopeConfigContext","createContext","Scope","children","timeRange","startTime","endTime","interval","attributes","groupBy","operator","appearance","tooltip","colors","fill","sortByValues","parentContext","useContext","LoadingComponent","ErrorComponent","TooltipComponent","EmptyComponent","tooltipLayout","tooltipFormat","tooltipHideAttributes","tooltipHideAttributeKey","tooltipHideMetric","memoizedAppearance","useMemo","memoizedTooltip","scopeConfigValue","DEFAULT_TIMERANGE","useScope","ctx","Loading","Error","Empty","jsx","UnblindProvider","children","queryClient","apiBaseUrl","fetchImpl","timeRange","startTime","endTime","interval","attributes","groupBy","operator","appearance","tooltip","colors","sortByValues","UnblindClientProvider","Scope","useQuery","useMetrics","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","useQuery","res","metrics","useQuery","useMemo","ms","timeRangeToCalculatedTimestamp","timeRange","now","deduceTimestamp","startTime","endTime","calculatedStartTime","calculatedEndTime","useTimeseries","queries","timeRange","startTime","endTime","interval","apiBaseUrl","fetchImpl","useUnblindClientConfig","metricNames","useMemo","x","attributes","attrs","attributeKeys","key","operators","groupBy","query","useQuery","calculatedStartTime","calculatedEndTime","deduceTimestamp","apiRequestBody","res","series","times","metadata","isLoading","isFetching","hasError","useQuery","useMemo","useUsage","timeRange","startTime","endTime","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","useQuery","calculatedStartTime","calculatedEndTime","deduceTimestamp","endpoint","res","usage","useMemo","isLoading","hasError","useInfiniteQuery","useMemo","useLogs","timeRange","filters","startTime","endTime","apiBaseUrl","fetchImpl","useUnblindClientConfig","hasValidTimeConfig","query","useInfiniteQuery","x","pageParam","filtersByName","acc","filter","body","severity","service","traceId","spanId","attributes","deducedStartTime","deducedEndTime","deduceTimestamp","res","data","next_page","lastPage","useMemo","page","useMemo","useEffect","useRef","uPlot","isLineThreshold","threshold","isRangeThreshold","uPlot","getValueFormat","dateTimeFormat","dateTimeFormatTimeAgo","systemDateFormats","timeUnitSize","timeIncrs","formatTime","splits","foundIncr","range","timeZone","format","systemDateFormats","yearRoundedToDay","v","dateTimeFormat","date","showSeconds","showMillis","checkTimeStr","findBestIncrement","targetSeconds","scales","scale","mult","incr","lastScale","lastMult","generateRelativeXAxisSplits","u","min","max","generateXAxisSplits","px","approxTicks","increment","multiplier","step","t","hourStep","startDate","currentHour","alignedHour","currentDate","generateRelativeXAxisValues","timestamp","index","dateTimeFormatTimeAgo","generateXAxisValues","splitsInMs","s","createXAxisConfig","fontFamily","relativeTimeAxis","_","values","getChartFont","computePosition","flip","offset","createRoot","TOOLTIP_DISTANCE_CURSOR","TOOLTIP_PADDING_FROM_CURSOR","TooltipManager","u","content","anchor","overlay","x","y","tooltipManager","dateTimeFormat","useMemo","jsx","jsxs","getMetricName","item","getSortKey","attrs","firstKey","sortItemsByValue","items","a","b","sortItemsAlphabetically","groupItemsByMetric","groups","metricName","createGroupedItems","sortByValues","layout","format","key","groupItems","formatLabel","sortedGroupItems","label","skipUppercase","parts","last","word","determineLayout","explicitLayout","hasAttributes","uniqueMetrics","TooltipItemRowAttributes","hideAttributeKey","attribute","isLastAttribute","value","TooltipItemRow","hideMetric","hideAttributes","attributes","showAttributes","i","Divider","className","props","TooltipGroup","group","groupIdx","hideGroupLabel","idx","Tooltip","timestamp","timeZone","spansMultipleDays","propsLayout","propsHideAttributeKey","propsHideMetric","propsHideAttributes","formattedTime","dateTimeFormat","useMemo","shouldHideMetric","groupedItems","jsx","checkSpansMultipleDays","xData","timeZone","minTimestamp","maxTimestamp","minDate","maxDate","getDateString","date","findNearestNonNullIndex","u","idx","hasNonNullValue","seriesIndex","dataLength","distance","leftCandidate","rightCandidate","buildTooltipItems","actualIdx","formatValue","stacked","metadata","series","hasAttributes","items","rawVal","prevVal","s","originalSerie","metric","stroke","color","formattedValue","tooltipPlugin","sortByValues","appearance","layout","format","hideAttributeKey","hideAttributes","hideMetric","over","boundingLeft","boundingTop","isHovering","syncBounds","bbox","tooltipManager","left","top","spansMultipleDays","timestamp","AppearanceTooltip","tooltipElement","Tooltip","DEFAULT_COLORS","getChartFont","fontFamiliy","compareAndResolveUnit","paramsUnit","metricMetadata","unit","isColorsObject","colors","isColorsArray","getColor","serie","index","colors","type","computedStyle","isFilling","isColorsArray","isColorsObject","cssColorVar","cssColor","DEFAULT_COLORS","generatePath","barsPath","uPlot","linearPath","splinePath","steppedPath","generateFill","getStrokeWidthByType","fill","cursorMovement","u","left","top","xVal","xData","nearestIdx","lo","hi","mid","midVal","loVal","hiVal","snapIdx","i","hasValue","j","seriesData","snapVal","mutateAxisGridAndTextColor","opts","textColor","gridColor","xAxis","yAxis","getThresholdColor","level","getThresholdRangeFillColor","getThresholdWidth","lineType","getThresholdDash","createThresholds","thresholds","thresholdSeries","threshold","thresholdSerie","isLineThreshold","createChartBands","series","stackedData","x","isRangeThreshold","rangeThresholdBands","idx","band","createChartSeries","stacked","strokeWidth","parseUnit","unit","sUnit","createChartOptions","container","metadata","sortByValues","timeZone","tooltipComponent","relativeTimeAxis","format","layout","hideAttributeKey","hideAttributes","hideMetric","predefinedMin","predefinedMax","fontFamily","initialWidth","initialHeight","formatUnit","formatter","getValueFormat","formattingFunction","v","f","buildScaleRange","tooltipPlugin","createXAxisConfig","createYAxisConfig","calculateNiceStep","maxMin","maxTicks","roughStep","magnitude","residual","niceStep","usePredefinedIfExists","predefinedNumber","defaultNumber","buildScaleRange","unit","predefinedMin","predefinedMax","_","dataMin","dataMax","padding","max","min","niceMax","niceMin","calculateYAxisSize","self","values","axisIdx","cycleNum","axis","axisSize","longestVal","acc","val","createYAxisConfig","formatter","fontFamiliy","getChartFont","vals","v","formmatedVal","stack","data","omit","bands","xAxis","xAxisLen","accum","accumData","serie","i","value","index","serieIndex","j","b","jsx","Chart","props","times","series","metadata","type","className","timeZone","propsOptions","tooltip","propsColors","tooltipComponent","sortByValues","propsUnit","fill","thresholds","predefinedMin","predefinedMax","tooltipLayout","tooltipFormat","hideAttributeKey","hideAttributes","hideMetric","chartRef","useRef","scope","useScope","colors","useEffect","data","unit","serie","metricMetadata","compareAndResolveUnit","threshold","isLineThreshold","isRangeThreshold","stacked","stackedData","stack","container","u","opts","createChartOptions","uPlot","resizeObserver","jsx","Timeseries","metrics","propOperator","propAttributes","propGroupBy","propInterval","type","className","propAppearance","propUnit","propThresholds","propMin","propMax","propColors","propTooltip","propSortByValues","propFill","scope","useScope","interval","attributes","groupBy","operator","colors","sortByValues","fill","tooltipComponent","tooltip","useMemo","isLoading","data","hasError","useTimeseries","metric","series","times","metadata","isEmpty","x","containerClassName","LoadingComponent","EmptyComponent","ErrorComponent","Chart"]}
1
+ {"version":3,"sources":["../src/providers/UnblindClientProvider.tsx","../src/providers/ScopeProvider.tsx","../src/components/Defaults/index.tsx","../src/providers/UnblindProvider.tsx","../src/hooks/useMetrics.ts","../src/hooks/useTimeseries.ts","../src/hooks/utils.ts","../src/hooks/useUsage.ts","../src/hooks/useLogs.ts","../src/components/Timeseries/index.tsx","../src/components/Chart/index.tsx","../src/types.ts","../src/components/Chart/utils.ts","../src/components/Chart/XAxis.ts","../src/components/Tooltip/TooltipManager.ts","../src/components/Tooltip/index.tsx","../src/components/Tooltip/plugin.tsx","../src/components/Chart/YAxis.ts"],"sourcesContent":["import {\n QueryClient,\n QueryClientConfig,\n QueryClientProvider,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\n\nexport type UnblindClientConfig = {\n /**\n * Base URL that client-side hooks will use.\n * Should point to the host app endpoint that is proxied by middleware.\n * Defaults to `/api/unblind`.\n */\n apiBaseUrl: string;\n\n /**\n * Optional custom fetch implementation (for tests or advanced setups).\n * When not provided, the global `fetch` will be used.\n */\n fetchImpl?: typeof fetch;\n};\n\nconst UnblindClientConfigContext = createContext<\n UnblindClientConfig | undefined\n>(undefined);\n\nexport type UnblindClientProviderProps = {\n children?: ReactNode;\n /**\n * Optional QueryClient instance. If not provided, a new one will be created.\n * Useful if you already have a QueryClientProvider in your app and want to reuse it.\n */\n queryClient?: QueryClient;\n\n /**\n * Optional QueryClientConfig. If not provided, a default one will be used.\n * Useful if you already have a QueryClientProvider in your app and want to reuse it.\n */\n queryClientConfig?: QueryClientConfig | undefined;\n\n /**\n * Optional API base URL override. Defaults to `/api/unblind`.\n */\n apiBaseUrl?: string;\n /**\n * Optional custom fetch implementation (for tests or advanced setups).\n */\n fetchImpl?: typeof fetch;\n};\n\n/**\n * UnblindClientProvider sets up the QueryClientProvider (for React Query)\n * and the Unblind client configuration context.\n *\n * @example\n * ```tsx\n * import { UnblindClientProvider } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindClientProvider apiBaseUrl=\"/api/unblind\">\n * <YourComponents />\n * </UnblindClientProvider>\n * );\n * }\n * ```\n */\nexport function UnblindClientProvider({\n children,\n queryClient: providedQueryClient,\n queryClientConfig: providedQueryClientConfig,\n apiBaseUrl = \"/api/unblind\",\n fetchImpl,\n}: UnblindClientProviderProps) {\n // Create a QueryClient if one wasn't provided\n const queryClient = useMemo(() => {\n if (providedQueryClient) {\n return providedQueryClient;\n }\n\n const defaultQueryConfig = {\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n refetchOnMount: false,\n };\n\n const baseConfig = {\n defaultOptions: {\n queries: defaultQueryConfig,\n },\n };\n\n // Merge with provided config, ensuring our defaults aren't overwritten\n if (providedQueryClientConfig) {\n return new QueryClient({\n ...providedQueryClientConfig,\n defaultOptions: {\n ...providedQueryClientConfig.defaultOptions,\n queries: {\n ...defaultQueryConfig,\n ...providedQueryClientConfig.defaultOptions?.queries,\n },\n },\n });\n }\n\n return new QueryClient(baseConfig);\n }, [providedQueryClient, providedQueryClientConfig]);\n\n const configValue: UnblindClientConfig = useMemo(\n () => ({\n apiBaseUrl,\n fetchImpl,\n }),\n [apiBaseUrl, fetchImpl],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <UnblindClientConfigContext.Provider value={configValue}>\n {children}\n </UnblindClientConfigContext.Provider>\n </QueryClientProvider>\n );\n}\n\n/**\n * Access the Unblind client configuration.\n *\n * @throws Error if called outside of an UnblindClientProvider.\n * This ensures that QueryClientProvider is always available for hooks.\n */\nexport function useUnblindClientConfig(): UnblindClientConfig {\n const ctx = useContext(UnblindClientConfigContext);\n\n if (!ctx) {\n throw new Error(\n \"useUnblindConfig must be used within an UnblindClientProvider. \" +\n \"Please wrap your app or component tree with <UnblindClientProvider>.\",\n );\n }\n\n return ctx;\n}\n\n/**\n * Hook to refresh all timeseries data.\n * Invalidates all queries with the 'unblind' and 'timeseries' keys,\n * causing them to refetch automatically.\n *\n * @example\n * ```tsx\n * function Dashboard() {\n * const refresh = useRefresh();\n * return (\n * <>\n * <button onClick={() => refresh()}>Refresh</button>\n * <Timeseries metrics={[\"cpu\"]} />\n * </>\n * );\n * }\n * ```\n */\nexport function useRefresh(): () => Promise<void> {\n const queryClient = useQueryClient();\n\n return useCallback(async () => {\n await queryClient.refetchQueries({\n queryKey: [\"unblind\", \"timeseries\"],\n });\n }, [queryClient]);\n}\n","import React, { createContext, useContext, useMemo } from \"react\";\nimport type {\n TimeseriesQueryConfig,\n TimeRange,\n Appearance,\n ChartVisualConfig,\n} from \"../types\";\nimport { TooltipProps } from \"../components/Tooltip\";\nimport { Loading, Error, Empty } from \"../components/Defaults\";\n\nexport type ScopeConfig = TimeseriesQueryConfig &\n ChartVisualConfig & {\n /**\n * Optional appearance configuration for all components\n * within this scope.\n *\n * Use this to globally override UI components such as loading,\n * or error states.\n *\n * Local overrides passed directly to a component take\n * precedence over this configuration.\n *\n * @example\n * ```tsx\n * <Scope\n * appearance={{\n * components: {\n * Loading: CustomLoading,\n * Error: CustomError,\n * },\n * }}\n * >\n * <App />\n * </Scope>\n * ```\n */\n appearance?: Appearance;\n };\n\nconst ScopeConfigContext = createContext<ScopeConfig | undefined>(undefined);\n\nexport type ScopeProps = ScopeConfig & {\n children?: React.ReactNode;\n};\n\n/**\n * Scope provides scoped configuration for all Unblind components (charts, logs, etc).\n * This includes default time ranges, intervals, attributes, groupBy, operator, appearance, and colors.\n *\n * When nested, child scopes inherit defaults from parent scopes and can override specific values.\n * This allows you to set global defaults (like appearance) at the top level and override\n * settings (like timeRange) in nested scopes.\n *\n * @example\n * ```tsx\n * import { Scope } from '@unblind/react';\n *\n * function App() {\n * return (\n * <Scope\n * appearance={{ components: { Loading: CustomLoading } }}\n * timeRange=\"24h\"\n * >\n * // Components here use 24h and CustomLoading\n *\n * <Scope timeRange=\"1h\">\n * // Components here use 1h but still inherit CustomLoading\n * </Scope>\n * </Scope>\n * );\n * }\n * ```\n */\nexport function Scope({\n children,\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n appearance,\n tooltip,\n colors,\n fill,\n sortByValues,\n}: ScopeProps) {\n const parentContext = useContext(ScopeConfigContext);\n\n // Components\n const LoadingComponent =\n appearance?.components?.Loading ||\n parentContext?.appearance?.components?.Loading;\n const ErrorComponent =\n appearance?.components?.Error ||\n parentContext?.appearance?.components?.Error;\n const TooltipComponent =\n appearance?.components?.Tooltip ||\n parentContext?.appearance?.components?.Tooltip;\n const EmptyComponent =\n appearance?.components?.Empty ||\n parentContext?.appearance?.components?.Empty;\n\n // Tooltip\n const tooltipLayout = tooltip?.layout || parentContext?.tooltip?.layout;\n const tooltipFormat = tooltip?.format || parentContext?.tooltip?.format;\n const tooltipHideAttributes =\n typeof tooltip?.hideAttributes === \"boolean\"\n ? tooltip?.hideAttributes\n : parentContext?.tooltip?.hideAttributes;\n const tooltipHideAttributeKey =\n typeof tooltip?.hideAttributeKey === \"boolean\"\n ? tooltip?.hideAttributeKey\n : parentContext?.tooltip?.hideAttributeKey;\n const tooltipHideMetric =\n typeof tooltip?.hideMetric === \"boolean\"\n ? tooltip?.hideMetric\n : parentContext?.tooltip?.hideMetric;\n\n const memoizedAppearance = useMemo(() => {\n return {\n components: {\n ...(LoadingComponent && { Loading: LoadingComponent }),\n ...(ErrorComponent && { Error: ErrorComponent }),\n ...(TooltipComponent && { Tooltip: TooltipComponent }),\n ...(EmptyComponent && { Empty: EmptyComponent }),\n },\n };\n }, [LoadingComponent, ErrorComponent, TooltipComponent, EmptyComponent]);\n\n const memoizedTooltip = useMemo(() => {\n return {\n layout: tooltipLayout,\n format: tooltipFormat,\n hideAttributes: tooltipHideAttributes,\n hideAttributeKey: tooltipHideAttributeKey,\n hideMetric: tooltipHideMetric,\n };\n }, [\n tooltipLayout,\n tooltipFormat,\n tooltipHideAttributes,\n tooltipHideAttributeKey,\n tooltipHideMetric,\n ]);\n\n const scopeConfigValue: ScopeConfig = useMemo(\n () => ({\n timeRange: timeRange ?? parentContext?.timeRange,\n startTime: startTime ?? parentContext?.startTime,\n endTime: endTime ?? parentContext?.endTime,\n interval: interval ?? parentContext?.interval,\n attributes: attributes ?? parentContext?.attributes,\n groupBy: groupBy ?? parentContext?.groupBy,\n operator: operator ?? parentContext?.operator,\n colors: colors ?? parentContext?.colors,\n sortByValues:\n typeof sortByValues === \"boolean\"\n ? sortByValues\n : parentContext?.sortByValues,\n fill: typeof fill === \"boolean\" ? fill : parentContext?.fill,\n appearance: memoizedAppearance,\n tooltip: memoizedTooltip,\n }),\n [\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n colors,\n sortByValues,\n fill,\n memoizedAppearance,\n memoizedTooltip,\n parentContext,\n ],\n );\n\n return (\n <ScopeConfigContext.Provider value={scopeConfigValue}>\n {children}\n </ScopeConfigContext.Provider>\n );\n}\n\nconst DEFAULT_TIMERANGE = \"6h\" as const;\n\nexport type UseScopeReturn = TimeseriesQueryConfig &\n ChartVisualConfig & {\n timeRange: TimeRange;\n\n appearance: {\n components: {\n Loading: React.ComponentType;\n Error: React.ComponentType;\n Empty: React.ComponentType;\n Tooltip?: React.ComponentType<TooltipProps>;\n };\n };\n };\n\n/**\n * Hook to access the scoped configuration from Scope.\n * Returns all configuration including time range, attributes,\n * groupBy, operator, colors, and UI components.\n *\n * @example\n * ```tsx\n * function MyChart() {\n * const { timeRange, colors, appearance } = useScope();\n * // Use configuration...\n * }\n * ```\n */\nexport function useScope(): UseScopeReturn {\n const ctx = useContext(ScopeConfigContext);\n\n return useMemo(() => {\n return {\n ...ctx,\n timeRange: ctx?.timeRange || DEFAULT_TIMERANGE,\n appearance: {\n components: {\n Loading: ctx?.appearance?.components?.Loading ?? Loading,\n Error: ctx?.appearance?.components?.Error ?? Error,\n Empty: ctx?.appearance?.components?.Empty ?? Empty,\n Tooltip: ctx?.appearance?.components?.Tooltip,\n },\n },\n };\n }, [ctx]);\n}\n","import \"./Defaults.css\";\n\nexport function Empty() {\n return (\n <div className=\"ub-default\">\n <div className=\"ub-empty-content\">\n <div className=\"ub-empty-icon-wrapper\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"ub-icon\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\"\n />\n </svg>\n </div>\n <p\n className=\"ub-empty-text\"\n data-text=\"No data available for this time range\"\n />\n </div>\n </div>\n );\n}\n\nexport function Error() {\n return <div className=\"ub-error\" />;\n}\n\nexport function Loading() {\n return <div className=\"ub-default\" data-text=\"Loading\" />;\n}\n","import {\n UnblindClientProvider,\n UnblindClientProviderProps,\n} from \"./UnblindClientProvider\";\nimport { Scope, ScopeProps } from \"./ScopeProvider\";\n\n// Re-export hooks\nexport { useUnblindClientConfig, useRefresh } from \"./UnblindClientProvider\";\nexport { useScope } from \"./ScopeProvider\";\n\nexport type UnblindProviderProps = UnblindClientProviderProps &\n ScopeProps & { children?: React.ReactNode };\n\n/**\n * UnblindProvider is required for all Unblind hooks to work.\n * It sets up both the QueryClientProvider (for React Query) and the Unblind configuration context.\n * This is a convenience wrapper around `UnblindClientProvider` and `Scope`.\n *\n * @example\n * ```tsx\n * import { UnblindProvider } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindProvider apiBaseUrl=\"/api/unblind\">\n * <YourComponents />\n * </UnblindProvider>\n * );\n * }\n * ```\n */\nexport function UnblindProvider({\n children,\n queryClient,\n apiBaseUrl,\n fetchImpl,\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n appearance,\n tooltip,\n colors,\n sortByValues,\n}: UnblindProviderProps) {\n return (\n <UnblindClientProvider\n queryClient={queryClient}\n apiBaseUrl={apiBaseUrl}\n fetchImpl={fetchImpl}\n >\n <Scope\n timeRange={timeRange}\n startTime={startTime}\n endTime={endTime}\n interval={interval}\n attributes={attributes}\n groupBy={groupBy}\n operator={operator}\n appearance={appearance}\n tooltip={tooltip}\n colors={colors}\n sortByValues={sortByValues}\n >\n {children}\n </Scope>\n </UnblindClientProvider>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useUnblindClientConfig } from \"../providers/UnblindProvider\";\nimport type { MetricMetadataList } from \"../types\";\n\nexport interface UseMetricsReturn {\n metrics: MetricMetadataList | undefined;\n isLoading: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch the list of available metrics metadata.\n *\n * @returns Object containing the metrics list, loading state, error state, and refetch function.\n */\nexport function useMetrics(): UseMetricsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n\n const query = useQuery<MetricMetadataList>({\n queryKey: [\"unblind\", \"metrics\"],\n queryFn: async () => {\n const res = await fetchImpl(`${apiBaseUrl}/metrics`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n throw new Error(\"Error loading metrics metadata\");\n }\n\n if (res.status === 200) {\n const { data: metrics } = await res.json();\n return metrics;\n } else {\n throw new Error(\"Unexpected status code\");\n }\n },\n });\n\n return {\n metrics: query.data,\n isLoading: query.isLoading,\n hasError: query.isError,\n refetch: query.refetch,\n };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport type {\n MetricMetadata,\n Serie,\n TimeseriesQuery,\n TimeseriesQueryConfig,\n} from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\n/**\n * Response structure from the timeseries API.\n */\ntype TimeseriesResponse = {\n request: SeriesAPIRequest;\n metadata: Record<string, MetricMetadata>;\n series: Array<Serie>;\n times: Array<number>;\n isEmpty?: boolean;\n};\n\n/**\n * Internal API request structure.\n */\ninterface SeriesAPIRequest {\n queries: Array<TimeseriesQuery>;\n startTime: number;\n endTime: number;\n interval?: number;\n}\n\nexport interface UseTimeseriesParams extends Pick<\n TimeseriesQueryConfig,\n \"timeRange\" | \"startTime\" | \"endTime\" | \"interval\"\n> {\n /**\n * Array of queries to execute.\n */\n queries: Array<TimeseriesQuery>;\n}\n\nexport interface UseTimeseriesReturn {\n data: {\n series: Serie[];\n times: number[];\n metadata: Record<string, MetricMetadata>;\n };\n isLoading: boolean;\n isFetching: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch timeseries data for metrics.\n *\n * @param params - Configuration object with queries, and either timeRange or startTime/endTime, plus optional interval.\n * @returns Object containing timeseries data, loading states, and error state.\n */\nexport function useTimeseries({\n queries,\n timeRange,\n startTime,\n endTime,\n interval,\n}: UseTimeseriesParams): UseTimeseriesReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n\n const metricNames = useMemo(\n () => queries.map((x) => x.metrics.join(\",\")).join(\",\"),\n [queries],\n );\n const attributes = useMemo(\n () =>\n queries\n .map((x) => {\n const attrs = x.attributes;\n if (!attrs) return \"\";\n\n const attributeKeys = Object.keys(attrs);\n if (attributeKeys.length === 0) return \"\";\n return attributeKeys\n .map((key: string) => key + \":\" + attrs[key]?.join(\",\"))\n .join(\",\");\n })\n .join(\",\"),\n [queries],\n );\n const operators = useMemo(() => queries.map((x) => x.operator), [queries]);\n const groupBy = useMemo(\n () => queries.map((x) => x.groupBy).join(\", \"),\n [queries],\n );\n\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useQuery<TimeseriesResponse>({\n queryKey: [\n \"unblind\",\n \"timeseries\",\n metricNames,\n attributes,\n startTime,\n endTime,\n timeRange,\n interval,\n operators,\n groupBy,\n ],\n queryFn: async () => {\n if (!metricNames) {\n throw new Error(\"Missing required parameters\");\n }\n if (metricNames.length === 0) {\n throw new Error(\"No series provided\");\n }\n\n const [calculatedStartTime, calculatedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const apiRequestBody: SeriesAPIRequest = {\n queries,\n startTime: calculatedStartTime,\n endTime: calculatedEndTime,\n interval,\n };\n const res = await fetchImpl(`${apiBaseUrl}/tenants/timeseries`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(apiRequestBody),\n });\n if (!res.ok) throw new Error(\"Error fetching metric\");\n const { series, times, metadata } = await res.json();\n if (!series) {\n console.error(\"Series not found\");\n throw new Error(\"Series not found\");\n }\n return { series, times, metadata } as TimeseriesResponse;\n },\n enabled: !!metricNames && hasValidTimeConfig,\n });\n\n const {\n metadata,\n series,\n times,\n }: {\n series: Array<Serie>;\n times: Array<number>;\n metadata: Record<string, MetricMetadata>;\n } = useMemo(() => {\n if (!query.data) return { series: [], times: [], metadata: {} };\n return {\n series: query.data.series,\n times: query.data.times,\n metadata: query.data.metadata,\n };\n }, [query]);\n\n const isLoading = query.isLoading;\n const isFetching = query.isFetching;\n const hasError = query.isError;\n\n return {\n data: { series, times, metadata },\n isLoading,\n isFetching,\n hasError,\n refetch: query.refetch,\n };\n}\n","import { TimeRange } from \"@/types\";\nimport ms from \"ms\";\n\nexport function timeRangeToCalculatedTimestamp(\n timeRange: TimeRange,\n): [number, number] {\n const now = Math.floor(Date.now() / 1000);\n const calculatedStartTime = now - Math.floor(ms(timeRange) / 1000);\n const calculatedEndTime = now;\n return [calculatedStartTime, calculatedEndTime];\n}\n\nexport function deduceTimestamp(\n timeRange?: TimeRange,\n startTime?: number,\n endTime?: number,\n): [number, number] {\n let calculatedStartTime: number;\n let calculatedEndTime: number;\n\n if (typeof startTime === \"number\" && typeof endTime === \"number\") {\n calculatedStartTime = startTime!;\n calculatedEndTime = endTime!;\n } else if (timeRange) {\n const [startTime, endTime] = timeRangeToCalculatedTimestamp(timeRange);\n calculatedStartTime = startTime;\n calculatedEndTime = endTime;\n } else {\n throw new Error(\n \"Either timeRange or both startTime and endTime must be provided\",\n );\n }\n\n return [calculatedStartTime, calculatedEndTime];\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport { TimeConfig } from \"@/types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport interface Usage {\n period: {\n startTime: number;\n endTime: number;\n };\n metrics: {\n units: number;\n };\n logs: {\n bytes: number;\n units: number;\n };\n}\n\ntype UsageResponse = {\n data: Array<Usage>;\n};\n\nexport type UseUsageParams = TimeConfig;\n\nexport interface UseUsageReturn {\n usage: Array<Usage>;\n isLoading: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch usage data.\n *\n * @param params - Configuration object with optional timeRange.\n * @returns Object containing usage data, loading state, and error state.\n */\nexport function useUsage({\n timeRange,\n startTime,\n endTime,\n}: UseUsageParams): UseUsageReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useQuery<Array<Usage>>({\n queryKey: [\"unblind\", \"usage\", timeRange, startTime, endTime],\n queryFn: async () => {\n const [calculatedStartTime, calculatedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const endpoint = `${apiBaseUrl}/tenants/usage`;\n\n const res = await fetchImpl(endpoint, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n startTime: calculatedStartTime,\n endTime: calculatedEndTime,\n }),\n });\n if (!res.ok) throw new Error(\"Error fetching usage\");\n const { data: usage } = (await res.json()) as UsageResponse;\n if (!usage) {\n throw new Error(\"usage not found\");\n }\n\n return usage;\n },\n enabled: hasValidTimeConfig,\n });\n\n const usage: Array<Usage> = useMemo(() => {\n if (!query.data) return [];\n return query.data || [];\n }, [query]);\n\n const isLoading = query.isLoading || query.isRefetching;\n const hasError = query.isError;\n\n return {\n usage,\n isLoading,\n hasError,\n refetch: query.refetch,\n };\n}\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport type {\n AttributeWithValue,\n Log,\n PaginatedResponse,\n TimeConfig,\n} from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport type UseLogsParams = {\n /**\n * Array of filters to apply to the logs query.\n */\n filters: Array<AttributeWithValue>;\n} & TimeConfig;\n\nexport interface UseLogsReturn {\n logs: Array<Log>;\n isLoading: boolean;\n hasError: boolean;\n hasNextPage: boolean;\n fetchNextPage: () => void;\n isFetchingNextPage: boolean;\n refetch: () => void;\n}\n\nexport type PaginatedLogsResponse = PaginatedResponse<Log>;\n\n/**\n * Hook to fetch logs data with infinite scroll pagination.\n *\n * @param params - Configuration object with timeRange and filters.\n * @returns Object containing logs data, loading states, and pagination controls.\n */\nexport function useLogs({\n timeRange,\n filters,\n startTime,\n endTime,\n}: UseLogsParams): UseLogsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useInfiniteQuery<PaginatedLogsResponse>({\n queryKey: [\n \"unblind\",\n \"logs\",\n timeRange,\n filters\n .map((x) => x.name + \":\" + x.value)\n .sort()\n .join(\",\"),\n ],\n queryFn: async ({ pageParam }) => {\n const filtersByName = filters.reduce<Record<string, string[]>>(\n (acc, filter) => {\n if (!acc[filter.name]) {\n acc[filter.name] = [];\n }\n acc[filter.name]!.push(filter.value);\n return acc;\n },\n {},\n );\n\n const {\n body = [],\n severity = [],\n \"service.name\": service = [],\n \"trace.id\": traceId = [],\n \"span.id\": spanId = [],\n ...attributes\n } = filtersByName;\n\n const [deducedStartTime, deducedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const res = await fetchImpl(`${apiBaseUrl}/tenants/logs`, {\n method: \"POST\",\n body: JSON.stringify({\n filter: {\n attributes,\n body,\n severity,\n traceId,\n spanId,\n service,\n },\n startTime: deducedStartTime,\n endTime: deducedEndTime,\n pagination: {\n page: pageParam,\n },\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) throw new Error(\"Error fetching logs\");\n\n const { data, next_page } = await res.json();\n\n if (!data) {\n throw new Error(\"data not found\");\n }\n\n return { data, next_page } as PaginatedLogsResponse;\n },\n enabled: hasValidTimeConfig,\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => lastPage.next_page,\n });\n\n const logs = useMemo(() => {\n if (!query.data) return [];\n return query.data.pages.flatMap((page) => page.data || []);\n }, [query.data]);\n\n return {\n logs,\n isLoading: query.isLoading,\n hasError: query.isError,\n hasNextPage: query.hasNextPage ?? false,\n fetchNextPage: query.fetchNextPage,\n isFetchingNextPage: query.isFetchingNextPage,\n refetch: query.refetch,\n };\n}\n","import { useTimeseries } from \"../../hooks/useTimeseries\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport type {\n ChartType,\n Appearance,\n TimeseriesQueryConfig,\n Threshold,\n ChartVisualConfig,\n} from \"../../types\";\nimport { useMemo } from \"react\";\nimport { Chart } from \"../Chart\";\n\nexport type TimeseriesProps = Exclude<\n TimeseriesQueryConfig,\n \"startTime\" | \"endTime\" | \"timeRange\"\n> &\n ChartVisualConfig & {\n /**\n * Metric name(s) to display in the chart.\n *\n * @example Single metric\n * ```jsx\n * <Timeseries metrics=\"nodejs.eventloop.p50\" />\n * ```\n *\n * @example\n * Multiple metrics\n * ```jsx\n * <Timeseries metrics={[\"nodejs.eventloop.p50\", \"...\"]} />\n * ```\n */\n metrics: Array<string> | string;\n\n /**\n * Chart type. Defaults to \"line\".\n */\n type?: ChartType;\n\n /**\n * Optional threshold\n */\n thresholds?: Array<Threshold>;\n\n /**\n * Optional min value\n */\n min?: number;\n\n /**\n * Optional max value\n */\n max?: number;\n\n /**\n * Unit of measurement for the value.\n *\n * Supports standard unit identifiers:\n * - Data: 'bytes', 'decbytes', 'bits', 'decbits'\n * - Time: 'ms', 's', 'm', 'h', 'd'\n * - Throughput: 'Bps', 'Mbps', 'Gbps'\n * - Percentage: 'percent', 'percentunit'\n * - And many more standard units\n *\n */\n unit?: string;\n\n /**\n * Optional className for the chart container.\n */\n className?: string;\n\n /**\n * Optional appearance configuration for this chart\n */\n appearance?: Appearance;\n };\n\n/**\n * Displays a time series chart.\n * Uses values from <UnblindProvider> or <Scope>\n * when they are not explicitly provided as props.\n *\n * @example\n * ```tsx\n * <Timeseries\n * metrics=\"host.cpu\"\n * attributes={{ \"host.region\": [\"us-east-2\"] }}\n * groupBy=[\"host.name\"]\n * />\n * ```\n *\n * @example Using `<Scope>`\n * ```tsx\n * <Scope timeRange=\"1h\">\n * <Timeseries metrics=\"host.cpu\" />\n * <Timeseries metrics=\"host.memory\" />\n * </Scope>\n * ```\n */\nexport function Timeseries({\n metrics,\n operator: propOperator,\n attributes: propAttributes,\n groupBy: propGroupBy,\n interval: propInterval,\n type = \"line\",\n className,\n appearance: propAppearance,\n unit: propUnit,\n thresholds: propThresholds,\n min: propMin,\n max: propMax,\n colors: propColors,\n tooltip: propTooltip,\n sortByValues: propSortByValues,\n fill: propFill,\n}: TimeseriesProps) {\n // Get defaults from scope\n const scope = useScope();\n\n // Use props if provided, otherwise fall back to scope values\n // Priority: props > scope > defaults\n const interval = propInterval ?? scope.interval;\n const attributes = propAttributes ?? scope.attributes;\n const groupBy = propGroupBy ?? scope.groupBy;\n const operator = propOperator ?? scope.operator;\n const colors = propColors ?? scope.colors;\n const sortByValues =\n typeof propSortByValues === \"boolean\"\n ? propSortByValues\n : scope.sortByValues;\n const fill = typeof propFill === \"boolean\" ? propFill : scope.fill;\n const tooltipComponent =\n propAppearance?.components?.Tooltip ?? scope.appearance.components.Tooltip;\n\n const tooltip = useMemo(() => {\n return {\n layout: propTooltip?.layout ?? scope.tooltip?.layout,\n format: propTooltip?.format ?? scope.tooltip?.format,\n hideAttributes:\n propTooltip?.hideAttributes ?? scope.tooltip?.hideAttributes,\n hideAttributeKey:\n propTooltip?.hideAttributeKey ?? scope.tooltip?.hideAttributeKey,\n hideMetric: propTooltip?.hideMetric ?? scope.tooltip?.hideMetric,\n };\n }, [\n propTooltip?.layout,\n propTooltip?.format,\n propTooltip?.hideAttributes,\n propTooltip?.hideAttributeKey,\n propTooltip?.hideMetric,\n scope.tooltip?.layout,\n scope.tooltip?.format,\n scope.tooltip?.hideAttributes,\n scope.tooltip?.hideAttributeKey,\n scope.tooltip?.hideMetric,\n ]);\n\n const { isLoading, data, hasError } = useTimeseries({\n queries: (Array.isArray(metrics) ? metrics : [metrics]).map((metric) => ({\n metrics: [metric],\n operator,\n attributes,\n groupBy,\n })),\n timeRange: scope.timeRange,\n startTime: scope.startTime,\n endTime: scope.endTime,\n interval,\n });\n\n const { series, times, metadata } = data;\n const isEmpty = series.every((x) => x.isEmpty);\n\n const containerClassName = `ub-chart-container${className ? ` ${className}` : \"\"}`;\n\n if (isLoading) {\n const LoadingComponent =\n propAppearance?.components?.Loading ??\n scope.appearance.components.Loading;\n return (\n <div className={containerClassName}>\n <LoadingComponent />\n </div>\n );\n }\n\n if (hasError) {\n const ErrorComponent =\n propAppearance?.components?.Error ?? scope.appearance.components.Error;\n return (\n <div className={containerClassName}>\n <ErrorComponent />\n </div>\n );\n }\n\n if (isEmpty) {\n const EmptyComponent =\n propAppearance?.components?.Empty ?? scope.appearance.components.Empty;\n\n return (\n <div className={containerClassName}>\n <EmptyComponent />\n </div>\n );\n }\n\n return (\n <Chart\n times={times}\n series={series}\n metadata={metadata}\n type={type}\n className={className}\n tooltip={tooltip}\n colors={colors}\n sortByValues={sortByValues}\n unit={propUnit}\n fill={fill}\n tooltipComponent={tooltipComponent}\n thresholds={propThresholds}\n min={propMin}\n max={propMax}\n />\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport uPlot from \"uplot\";\nimport {\n type MetricMetadata,\n type Serie,\n type ChartVisualConfig,\n type ChartType,\n type Threshold,\n isLineThreshold,\n isRangeThreshold,\n} from \"../../types\";\nimport { TooltipProps } from \"../Tooltip\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport { stack } from \"./YAxis\";\nimport { compareAndResolveUnit, createChartOptions } from \"./utils\";\n\nexport interface ChartProps extends ChartVisualConfig {\n times: Array<number>;\n series: Serie[];\n metadata: Record<string, MetricMetadata>;\n type: ChartType;\n className?: string;\n timeZone?: string;\n options?: uPlot.Options;\n unit?: string;\n tooltipComponent?: React.ComponentType<TooltipProps>;\n thresholds?: Array<Threshold>;\n min?: number;\n max?: number;\n}\n\n/**\n * Renders a chart for time series data\n */\nexport function Chart(props: ChartProps) {\n const {\n times,\n series,\n metadata,\n type,\n className,\n timeZone,\n options: propsOptions,\n tooltip,\n colors: propsColors,\n tooltipComponent,\n sortByValues = false,\n unit: propsUnit,\n fill = false,\n thresholds,\n min: predefinedMin,\n max: predefinedMax,\n } = props;\n const {\n layout: tooltipLayout,\n format: tooltipFormat,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n } = tooltip || {};\n const chartRef = useRef<HTMLDivElement>(null);\n const scope = useScope();\n const colors = propsColors || scope.colors;\n\n useEffect(() => {\n if (!series || series.length === 0) {\n console.warn(\"No series provided\");\n return;\n }\n\n // uPlot aligned data works in the following way:\n // - First array of values is for X-axis\n // - The rest arrays are values for Y-axis\n const data: uPlot.AlignedData = [times];\n let unit: string | undefined = propsUnit;\n series.forEach((serie) => {\n const metricMetadata = metadata[serie.metric];\n\n if (!propsUnit) {\n unit = compareAndResolveUnit(unit, metricMetadata);\n }\n\n data.push(serie.values);\n });\n\n if (thresholds) {\n thresholds.forEach((threshold) => {\n if (isLineThreshold(threshold)) {\n data.push(new Array(times.length).fill(threshold.value));\n } else if (isRangeThreshold(threshold)) {\n data.push(new Array(times.length).fill(threshold.from));\n data.push(new Array(times.length).fill(threshold.to));\n }\n });\n }\n\n const stacked = type === \"bar\" || type === \"area\";\n const stackedData = stack(data, !stacked);\n const container = chartRef.current;\n let u: uPlot | null = null;\n\n if (container) {\n const opts = createChartOptions(\n container,\n metadata,\n stackedData,\n unit,\n series,\n type,\n stacked,\n colors,\n fill,\n sortByValues,\n timeZone,\n tooltipComponent,\n false,\n tooltipFormat,\n tooltipLayout,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n thresholds,\n predefinedMin,\n predefinedMax,\n );\n\n u = new uPlot({ ...opts, ...propsOptions }, stackedData.data, container);\n const resizeObserver = new ResizeObserver(() => {\n u?.setSize({\n width: container.clientWidth,\n height: container.clientHeight,\n });\n });\n\n resizeObserver.observe(container);\n\n return () => {\n u?.destroy();\n resizeObserver.disconnect();\n };\n }\n }, [\n series,\n times,\n type,\n metadata,\n timeZone,\n tooltipComponent,\n colors,\n fill,\n sortByValues,\n propsUnit,\n tooltipFormat,\n tooltipLayout,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n thresholds,\n predefinedMin,\n predefinedMax,\n propsOptions,\n ]);\n\n return (\n <div\n ref={chartRef}\n className={\"ub-chart-container\" + (className ? ` ${className}` : \"\")}\n />\n );\n}\n","/**\n * Shared types for Unblind React library.\n */\n\nimport { StringValue } from \"ms\";\nimport {\n TooltipFormat,\n TooltipLayout,\n TooltipProps,\n} from \"./components/Tooltip\";\n\nexport type MetricType =\n | \"gauge\"\n | \"sum\"\n | \"histogram\"\n | \"summary\"\n | \"exphistogram\";\n\n/**\n * Aggregation operators available for metric queries.\n */\nexport type AggregationOperator =\n | \"avg\"\n | \"sum\"\n | \"max\"\n | \"min\"\n | \"p90\"\n | \"p99\"\n | \"p50\";\n\n/**\n * Attribute filter with a specific value.\n */\nexport interface AttributeWithValue {\n name: string;\n value: string;\n}\n\n/**\n * Metric metadata returned by the API.\n */\nexport interface MetricMetadata {\n name: string;\n description: string;\n displayName?: string;\n unit: {\n /// The unique, case-sensitive UCUM identifier (e.g., \"mL\", \"m/s2\")\n code?: string;\n /// The human-readable primary name (e.g., \"milliliter\")\n name?: string;\n /// Alternate names (e.g., \"cubic centimeter\" for \"cm3\")\n synonym?: string;\n /// The physical dimension or property (e.g., \"Volume\", \"Mass\")\n category?: string;\n };\n type: MetricType;\n operator?: AggregationOperator;\n}\n\n/**\n * List of metric metadata.\n */\nexport type MetricMetadataList = Array<MetricMetadata>;\n\n/**\n * A single time series data point.\n */\nexport interface Serie {\n metric: string;\n attributes?: Record<string, string>;\n values: Array<number>;\n queryName?: string;\n queryIndex: number;\n isEmpty?: boolean;\n}\n\n/**\n * Query definition for timeseries requests.\n */\nexport interface TimeseriesQuery {\n queryName?: string;\n metrics: Array<string>;\n groupBy?: Array<string>;\n operator?: AggregationOperator;\n attributes?: Partial<Record<string, Array<string>>>;\n}\n\n/**\n * Chart types available\n */\nexport type ChartType = \"bar\" | \"line\" | \"area\" | \"step\" | \"spline\";\n\n/**\n * Time Range definition:\n *\n * e.g. 5secs, 4h, 3 days, and so on.\n */\nexport type TimeRange = StringValue;\n\nexport interface Log {\n timestamp: number;\n trace_id?: string;\n span_id?: string;\n severity_text: Severity;\n attributes?: Record<string, string>;\n service_name?: string;\n body?: string;\n}\n\n/**\n * Default paginated response structure\n */\nexport type PaginatedResponse<T> = {\n data: Array<T>;\n next_page?: string;\n};\n\n/**\n * Different severity values based on OTEL\n * https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext\n */\nexport type Severity = \"TRACE\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\" | \"FATAL\";\n\nexport type Colors =\n | Array<string>\n | { fill: Array<string>; border: Array<string> }\n | ((\n serie: Serie,\n index: number,\n type: ChartType,\n isFilling?: boolean,\n ) => string);\n\n/**\n * Interval expressed in seconds.\n */\nexport type Interval = number;\n\nexport type TimeConfig = {\n /**\n * Optional time range. If not provided, will use timeRange from the provider.\n * Ignored if startTime and endTime are provided.\n */\n timeRange?: TimeRange;\n /**\n * Optional start time (Unix timestamp in seconds).\n * If provided along with endTime, takes priority over timeRange.\n */\n startTime?: number;\n /**\n * Optional end time (Unix timestamp in seconds).\n * If provided along with startTime, takes priority over timeRange.\n */\n endTime?: number;\n};\n\n/**\n * Base query configuration for timeseries data.\n * Used across components and providers.\n */\nexport type TimeseriesQueryConfig = TimeConfig & {\n /**\n * Optional interval in seconds. If not provided, will use interval from the provider.\n */\n interval?: Interval;\n /**\n * Optional attributes filter. If not provided, will use attributes from the provider.\n */\n attributes?: Record<string, Array<string>>;\n /**\n * Optional groupBy. If not provided, will use groupBy from the provider.\n */\n groupBy?: Array<string>;\n /**\n * Optional aggregation operator. If not provided, will use the operator from the provider.\n */\n operator?: AggregationOperator;\n};\n\n/**\n * Visual/presentation configuration for charts.\n */\nexport interface ChartVisualConfig {\n /**\n * Optional color configuration\n */\n colors?: Colors;\n\n /**\n * Optional labeling order.\n * Defaults to false\n */\n sortByValues?: boolean;\n\n /**\n * Optional to fill charts.\n *\n * Bar and area charts are filled by default.\n */\n fill?: boolean;\n\n /**\n * Optional tooltip configuration.\n */\n tooltip?: TooltipConfig;\n}\n\nexport interface TooltipConfig {\n layout?: TooltipLayout;\n format?: TooltipFormat;\n hideAttributeKey?: boolean;\n hideAttributes?: boolean;\n hideMetric?: boolean;\n}\n\n/**\n * Appearance configuration for Unblind components.\n *\n * Allows consumers to override internal UI components used for\n * loading, empty, and error states.\n *\n * Overrides can be provided globally via Scope or\n * locally per component.\n *\n * This API customizes presentation only; component state and data\n * fetching logic remain internal.\n */\nexport type Appearance = {\n components?: {\n Loading?: React.ComponentType;\n Error?: React.ComponentType;\n Empty?: React.ComponentType;\n Tooltip?: React.ComponentType<TooltipProps>;\n };\n};\n\n/**\n * Typeguard for LineThreshold\n */\nexport const isLineThreshold = (\n threshold: Threshold,\n): threshold is LineThreshold => {\n return \"value\" in threshold;\n};\n\n/**\n * Typeguard for RangeThreshold\n */\nexport const isRangeThreshold = (\n threshold: Threshold,\n): threshold is RangeThreshold => {\n return \"from\" in threshold && \"to\" in threshold;\n};\n\nexport type ThresholdLevel = \"info\" | \"warning\" | \"error\" | \"ok\";\nexport type ThresholdLineType = \"line\" | \"dashed\" | \"bold\";\nexport type LineThreshold = {\n value: number;\n type?: ThresholdLineType;\n level?: ThresholdLevel;\n label?: string;\n};\n\nexport type RangeThreshold = {\n from: number;\n to?: number;\n type?: ThresholdLineType;\n level?: ThresholdLevel;\n label?: string;\n};\nexport type Threshold = LineThreshold | RangeThreshold;\n","import {\n ChartType,\n Colors,\n isLineThreshold,\n isRangeThreshold,\n MetricMetadata,\n Serie,\n Threshold,\n ThresholdLevel,\n ThresholdLineType,\n} from \"@/types\";\n\nimport uPlot, { Band } from \"uplot\";\nimport { getValueFormat } from \"@unblind/units\";\nimport { createXAxisConfig } from \"./XAxis\";\nimport { buildScaleRange, createYAxisConfig } from \"./YAxis\";\nimport { TooltipLayout, TooltipFormat, TooltipProps } from \"../Tooltip\";\nimport { tooltipPlugin } from \"../Tooltip/plugin\";\n\n/**\n * Default chart color palette.\n *\n * Note:\n * Colors are cycled in order when the number of series exceeds\n * the palette length.\n */\nexport const DEFAULT_COLORS: string[] = [\n \"oklch(0.70 0.24 293)\",\n \"oklch(0.85 0.18 95)\",\n \"oklch(0.65 0.25 255)\",\n \"oklch(0.72 0.26 27)\",\n \"oklch(0.70 0.22 150)\",\n \"oklch(0.78 0.18 50)\",\n \"oklch(0.72 0.18 215)\",\n \"oklch(0.70 0.27 301)\",\n \"oklch(0.75 0.20 90)\",\n \"oklch(0.68 0.23 277)\",\n \"oklch(0.70 0.18 193)\",\n \"oklch(0.68 0.25 4)\",\n];\n\n/**\n * Gets the chart font from CSS variables\n *\n * We want to use the same font style the application is using.\n * Some sort of manual font heritage.\n */\nexport const getChartFont = (fontFamiliy: string) => {\n const style = getComputedStyle(document.documentElement);\n const fontStyle = `${style.getPropertyValue(\"--ub-chart-font-size\").trim()} ${fontFamiliy}`;\n\n return fontStyle;\n};\n\nexport const compareAndResolveUnit = (\n paramsUnit?: string,\n metricMetadata?: MetricMetadata,\n) => {\n let unit = paramsUnit;\n\n if (\n metricMetadata &&\n metricMetadata.unit &&\n metricMetadata.unit.code &&\n metricMetadata.unit.code !== \"1\"\n ) {\n if (!unit && metricMetadata.unit) {\n unit = metricMetadata.unit.code;\n } else if (unit && metricMetadata.unit.code !== unit) {\n unit = undefined;\n }\n }\n\n return unit;\n};\n\n// Type guards for colors\nexport function isColorsObject(\n colors: Colors,\n): colors is { fill: string[]; border: string[] } {\n return (\n typeof colors === \"object\" &&\n colors !== null &&\n !Array.isArray(colors) &&\n Array.isArray(\n (colors as { fill: Array<string>; border: Array<string> }).fill,\n ) &&\n Array.isArray(colors.border)\n );\n}\n\nexport function isColorsArray(colors: Colors): colors is string[] {\n return Array.isArray(colors);\n}\n\nexport function isColorsFunction(\n colors: Colors,\n): colors is (\n serie: Serie,\n index: number,\n type: ChartType,\n isFilling?: boolean,\n) => string {\n return typeof colors === \"function\";\n}\n\n// Util to get a color from the colros variable.\nexport const getColor = (\n serie: Serie,\n index: number,\n colors: Colors | undefined,\n type: ChartType,\n computedStyle: CSSStyleDeclaration,\n isFilling?: boolean,\n) => {\n if (colors) {\n // User provided\n if (isColorsArray(colors)) {\n return colors[index];\n } else if (isColorsObject(colors)) {\n return isFilling ? colors.fill[index] : colors.border[index];\n } else {\n return colors(serie, index, type, isFilling);\n }\n }\n\n // If no colors provided, try to get from CSS variables\n const cssColorVar = isFilling\n ? `--ub-chart-serie-color-fill-${index + 1}`\n : `--ub-chart-serie-color-${index + 1}`;\n\n const cssColor = computedStyle.getPropertyValue(cssColorVar).trim();\n\n if (cssColor) {\n return cssColor;\n }\n\n // Static fallback\n return isFilling && (type === \"step\" || type === \"line\" || type === \"spline\")\n ? `oklch(from ${DEFAULT_COLORS[index % DEFAULT_COLORS.length]} l c h / 0.4)`\n : DEFAULT_COLORS[index % DEFAULT_COLORS.length];\n};\n\n// ============================================================================\n// Chart Path & Fill Generation\n// ============================================================================\n\n/**\n * Generates the path for a particular chart type\n */\nconst generatePath = (type: \"bar\" | \"line\" | \"area\" | \"step\" | \"spline\") => {\n const barsPath = uPlot.paths.bars!({ size: [0.6, 100], radius: 0, gap: 0 });\n const linearPath = uPlot.paths.linear!({\n alignGaps: 0,\n });\n const splinePath = uPlot.paths.spline!({ alignGaps: 1 });\n const steppedPath = uPlot.paths.stepped!({ alignGaps: 1 });\n\n switch (type) {\n case \"line\":\n return linearPath;\n case \"bar\":\n return barsPath;\n case \"area\":\n return linearPath;\n case \"step\":\n return steppedPath;\n case \"spline\":\n return splinePath;\n default:\n return linearPath;\n }\n};\n\n/**\n * Fill color for a particular chart type\n */\nconst generateFill = (\n serie: Serie,\n index: number,\n colors: Colors | undefined,\n type: ChartType,\n computedStyle: CSSStyleDeclaration,\n) => {\n return getColor(serie, index, colors, type, computedStyle, true);\n};\n\n// ============================================================================\n// Chart Utilities\n// ============================================================================\n\nconst getStrokeWidthByType = (type: ChartType, fill: boolean): number => {\n switch (type) {\n case \"bar\":\n return 1;\n case \"line\":\n return fill ? 1.5 : 2;\n case \"spline\":\n return fill ? 1.5 : 2;\n case \"area\":\n return 1;\n case \"step\":\n return 1.5;\n default:\n return 1;\n }\n};\n\nconst cursorMovement: uPlot.Cursor.MousePosRefiner = (u, left, top) => {\n if (left < 0 || top < 0) {\n // Return early if it is out of bounds\n return [left, top];\n }\n\n const xVal = u.posToVal(left, \"x\");\n\n const xData = u.data[0];\n if (!xData || xData.length === 0) {\n return [left, top];\n }\n\n let nearestIdx = 0;\n\n let lo = 0;\n let hi = xData.length - 1;\n\n while (hi - lo > 1) {\n const mid = Math.floor((lo + hi) / 2);\n const midVal = xData[mid];\n if (midVal != null && midVal < xVal) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const loVal = xData[lo];\n const hiVal = xData[hi];\n if (loVal != null && hiVal != null) {\n nearestIdx = Math.abs(loVal - xVal) < Math.abs(hiVal - xVal) ? lo : hi;\n } else if (loVal != null) {\n nearestIdx = lo;\n } else if (hiVal != null) {\n nearestIdx = hi;\n }\n\n // Search backward from nearestIdx to find first non-null value\n let snapIdx = nearestIdx;\n for (let i = nearestIdx; i >= 0; i--) {\n let hasValue = false;\n\n for (let j = 1; j < u.data.length; j++) {\n const seriesData = u.data[j];\n if (seriesData && seriesData[i] != null) {\n hasValue = true;\n break;\n }\n }\n if (hasValue) {\n snapIdx = i;\n break;\n }\n }\n\n const snapVal = xData[snapIdx];\n if (snapVal == null) {\n return [left, top];\n }\n\n const snappedLeft = u.valToPos(snapVal, \"x\");\n\n return [snappedLeft, top];\n};\n\nconst mutateAxisGridAndTextColor = (\n opts: uPlot.Options,\n computedStyle: CSSStyleDeclaration,\n) => {\n const textColor = computedStyle\n .getPropertyValue(\"--ub-chart-font-color\")\n .trim();\n const gridColor = computedStyle\n .getPropertyValue(\"--ub-chart-grid-color\")\n .trim();\n\n const xAxis = opts.axes?.[0];\n const yAxis = opts.axes?.[1];\n if (xAxis) {\n xAxis.stroke = textColor;\n if (xAxis.grid) {\n xAxis.grid.stroke = gridColor;\n } else {\n xAxis.grid = {\n stroke: gridColor,\n };\n }\n }\n\n if (yAxis) {\n yAxis.stroke = textColor;\n if (yAxis.grid) {\n yAxis.grid.stroke = gridColor;\n } else {\n yAxis.grid = {\n stroke: gridColor,\n };\n }\n }\n\n return opts;\n};\n\n// ============================================================================\n// Thresholds\n// ============================================================================\n\nconst getThresholdColor = (\n computedStyle: CSSStyleDeclaration,\n level?: ThresholdLevel,\n): string => {\n return (\n computedStyle.getPropertyValue(`--ub-chart-threshold-${level}`).trim() ||\n computedStyle.getPropertyValue(\"--ub-chart-threshold-default\").trim()\n );\n};\n\nconst getThresholdRangeFillColor = (\n computedStyle: CSSStyleDeclaration,\n level?: ThresholdLevel,\n): string => {\n return (\n computedStyle\n .getPropertyValue(`--ub-chart-threshold-${level}-fill`)\n .trim() ||\n computedStyle.getPropertyValue(\"--ub-chart-threshold-default-fill\").trim()\n );\n};\n\nconst getThresholdWidth = (lineType?: ThresholdLineType): number => {\n switch (lineType) {\n case \"bold\":\n return 2;\n case \"line\":\n return 1;\n case \"dashed\":\n return 1;\n default:\n return 1;\n }\n};\n\nconst getThresholdDash = (\n lineType?: ThresholdLineType,\n): number[] | undefined => {\n return lineType === \"line\" ? undefined : [5, 5];\n};\n\nconst createThresholds = (\n computedStyle: CSSStyleDeclaration,\n thresholds?: Array<Threshold>,\n): Array<uPlot.Series> => {\n if (!thresholds) {\n return [];\n } else {\n const thresholdSeries: Array<uPlot.Series> = [];\n\n thresholds.forEach((threshold) => {\n const thresholdSerie = {\n label: threshold.label,\n stroke: getThresholdColor(computedStyle, threshold.level),\n width: getThresholdWidth(),\n dash: getThresholdDash(threshold.type),\n points: { show: false, size: 0 },\n spanGaps: true,\n show: true,\n auto: false,\n };\n\n if (isLineThreshold(threshold)) {\n thresholdSeries.push(thresholdSerie);\n } else {\n thresholdSeries.push(thresholdSerie);\n thresholdSeries.push(thresholdSerie);\n }\n });\n\n return thresholdSeries;\n }\n};\n\n// ============================================================================\n// Chart Options Creation\n// ============================================================================\nexport const createChartBands = (\n series: Array<Serie>,\n stackedData: {\n data: uPlot.AlignedData;\n bands: Array<Band>;\n },\n computedStyle: CSSStyleDeclaration,\n thresholds?: Array<Threshold>,\n) => {\n if (!thresholds || !thresholds.some((x) => isRangeThreshold(x))) {\n return stackedData.bands;\n }\n\n const rangeThresholdBands = thresholds\n .map((threshold, idx) => {\n if (!isRangeThreshold(threshold)) {\n return null;\n }\n\n return {\n series: [series.length + idx * 2 + 1, series.length + idx * 2 + 2],\n fill: getThresholdRangeFillColor(computedStyle, threshold.level),\n dir: 1,\n };\n })\n .filter((band) => band !== null);\n\n return [...stackedData.bands, ...rangeThresholdBands];\n};\n\nconst createChartSeries = (\n series: Array<Serie>,\n type: ChartType,\n colors: Colors | undefined,\n fill: boolean,\n stacked: boolean,\n computedStyle: CSSStyleDeclaration,\n thresholds?: Array<Threshold>,\n): Array<uPlot.Series> => {\n const strokeWidth = getStrokeWidthByType(type, fill);\n return [\n {},\n ...series.map((serie, index) => ({\n label: serie.metric,\n stroke: getColor(serie, index, colors, type, computedStyle),\n width: strokeWidth,\n points: { show: false },\n spanGaps: true,\n paths: generatePath(type),\n fill:\n fill || stacked\n ? generateFill(serie, index, colors, type, computedStyle)\n : undefined,\n })),\n ...createThresholds(computedStyle, thresholds),\n ];\n};\n\nconst parseUnit = (unit?: string) => {\n if (typeof unit === \"string\") {\n const sUnit = String(unit).toLowerCase().trim();\n if (sUnit === \"by\") {\n return \"bytes\";\n } else if (sUnit.startsWith(\"{\") && sUnit.endsWith(\"}\")) {\n // Very common in OTEL for {cpu} or {memory} units\n return \"short\";\n }\n }\n\n return unit;\n};\n\n/**\n * Creates the complete uPlot options object for the chart\n */\nexport const createChartOptions = (\n container: HTMLDivElement,\n metadata: Record<string, MetricMetadata>,\n stackedData: {\n data: uPlot.AlignedData;\n bands: Array<uPlot.Band>;\n },\n unit: string | undefined,\n series: Array<Serie>,\n type: ChartType,\n stacked: boolean,\n colors: Colors | undefined,\n fill: boolean,\n sortByValues: boolean,\n timeZone?: string,\n tooltipComponent?: React.ComponentType<TooltipProps>,\n relativeTimeAxis?: boolean,\n format?: TooltipFormat,\n layout?: TooltipLayout,\n hideAttributeKey?: boolean,\n hideAttributes?: boolean,\n hideMetric?: boolean,\n thresholds?: Array<Threshold>,\n predefinedMin?: number,\n predefinedMax?: number,\n): uPlot.Options => {\n const computedStyle = window.getComputedStyle(container);\n const fontFamily = computedStyle.fontFamily;\n\n const initialWidth = container?.clientWidth ?? 1050;\n const initialHeight = container?.clientHeight ?? 250;\n const formatUnit = parseUnit(unit);\n const formatter = getValueFormat(formatUnit === \"1\" ? null : formatUnit);\n const formattingFunction = (v: number) => {\n const f = formatter(v, Number.isInteger(v) ? 0 : 2);\n return f.text + (f.suffix?.trim() || \"\");\n };\n\n const opts: uPlot.Options = {\n width: initialWidth,\n height: initialHeight,\n scales: {\n y: {\n range: buildScaleRange(unit, predefinedMin, predefinedMax),\n },\n },\n plugins: [\n tooltipPlugin(\n formattingFunction,\n stacked,\n metadata,\n sortByValues,\n timeZone,\n tooltipComponent,\n series,\n layout,\n format,\n hideAttributeKey,\n hideAttributes,\n hideMetric,\n ),\n ],\n padding: relativeTimeAxis ? [10, 15, 10, 15] : [8, 15, 8, 15],\n cursor: {\n y: false,\n sync: { key: \"_\" },\n drag: {\n setScale: true,\n x: true,\n y: false,\n },\n move: cursorMovement,\n },\n series: createChartSeries(\n series,\n type,\n colors,\n fill,\n stacked,\n computedStyle,\n thresholds,\n ),\n bands: createChartBands(series, stackedData, computedStyle, thresholds) as\n | uPlot.Band[]\n | undefined,\n axes: [\n createXAxisConfig(fontFamily, timeZone, relativeTimeAxis),\n createYAxisConfig(formatter, fontFamily),\n ],\n legend: {\n show: false,\n },\n };\n\n return mutateAxisGridAndTextColor(opts, computedStyle);\n};\n","import {\n dateTimeFormat,\n dateTimeFormatTimeAgo,\n systemDateFormats,\n} from \"@unblind/units\";\nimport { getChartFont } from \"./utils\";\n\n// Time unit sizes in milliseconds\nconst timeUnitSize = {\n millisecond: 1,\n second: 1000,\n minute: 60000,\n hour: 3600000,\n day: 86400000,\n month: 2419200000, // 28 days\n year: 31536000000,\n};\n\n// Time axis increments (in seconds)\nconst timeIncrs = {\n second: [1, 2, 5, 10, 15, 30],\n minute: [1, 2, 5, 10, 15, 30],\n hour: [1, 2, 3, 4, 6, 8, 12],\n day: [1, 2, 3, 7, 14],\n month: [1, 2, 3, 6],\n year: [1, 2, 5, 10, 20, 50, 100],\n};\n\nfunction formatTime(\n splits: number[],\n foundIncr: number,\n range: number,\n timeZone?: string,\n): string[] {\n // Handle Month/Year increments\n if (foundIncr > 7 * timeUnitSize.day) {\n let format = systemDateFormats.interval.year;\n\n const yearRoundedToDay =\n Math.round(timeUnitSize.year / timeUnitSize.day) * timeUnitSize.day;\n const incrementRoundedToDay =\n Math.round(foundIncr / timeUnitSize.day) * timeUnitSize.day;\n\n if (incrementRoundedToDay === yearRoundedToDay) {\n format = systemDateFormats.interval.year;\n } else if (foundIncr <= timeUnitSize.year) {\n format = systemDateFormats.interval.month;\n } else {\n format = systemDateFormats.interval.day;\n }\n\n return splits.map((v) => dateTimeFormat(v, { format, timeZone }));\n }\n\n // Handle Intraday (Hours, Minutes, Seconds)\n return splits.map((v) => {\n const date = new Date(v);\n\n const showSeconds = foundIncr < timeUnitSize.minute;\n const showMillis = foundIncr < timeUnitSize.second;\n\n // Check for Midnight in the specific TimeZone\n // We use \"en-GB\" here strictly to check the \"00:00\" pattern reliably\n // without worrying about AM/PM locale differences in the logic.\n const checkTimeStr = date.toLocaleTimeString(\"en-GB\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n timeZone,\n });\n\n // IF it is midnight (and we aren't zooming into seconds), SHOW DATE\n if (\n (checkTimeStr === \"00:00\" || checkTimeStr === \"24:00\") &&\n !showSeconds &&\n !showMillis\n ) {\n return date.toLocaleDateString(undefined, {\n day: \"2-digit\",\n month: \"short\",\n timeZone,\n });\n }\n\n return date.toLocaleTimeString(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: showSeconds ? \"2-digit\" : undefined,\n fractionalSecondDigits: showMillis ? 3 : undefined,\n hour12: false,\n timeZone,\n });\n });\n}\n\n/**\n * X-Axis: Tick interval selection.\n * Picks the best increment size based on target spacing\n */\nfunction findBestIncrement(targetSeconds: number): {\n increment: number;\n multiplier: number;\n} {\n const scales = [\n { size: timeUnitSize.second / 1000, increments: timeIncrs.second },\n { size: timeUnitSize.minute / 1000, increments: timeIncrs.minute },\n { size: timeUnitSize.hour / 1000, increments: timeIncrs.hour },\n { size: timeUnitSize.day / 1000, increments: timeIncrs.day },\n { size: timeUnitSize.month / 1000, increments: timeIncrs.month },\n { size: timeUnitSize.year / 1000, increments: timeIncrs.year },\n ];\n\n for (const scale of scales) {\n for (const mult of scale.increments) {\n const incr = scale.size * mult;\n if (incr >= targetSeconds) {\n return { increment: incr * 1000, multiplier: mult };\n }\n }\n }\n\n // Fallback to largest year increment\n const lastScale = scales[scales.length - 1] as (typeof scales)[0];\n const lastMult = lastScale.increments[\n lastScale.increments.length - 1\n ] as number;\n return {\n increment: lastScale.size * lastMult * 1000,\n multiplier: lastMult,\n };\n}\n\n/**\n * X-Axis: Generates splits using the x latest timestamp and soonest timestamp.\n */\nfunction generateRelativeXAxisSplits(\n u: uPlot,\n min: number,\n max: number,\n): number[] {\n return [min, max];\n}\n\n/**\n * X-Axis: Generate splits (tick positions) for the time axis\n */\nfunction generateXAxisSplits(\n u: uPlot,\n min: number,\n max: number,\n timeZone?: string,\n): number[] {\n const range = max - min;\n const px = u.width;\n const approxTicks = Math.floor(px / 100);\n const targetSeconds = range / approxTicks;\n const { increment, multiplier } = findBestIncrement(targetSeconds);\n const step = increment / 1000;\n const splits: number[] = [];\n\n // For very short ranges (< 12 hours)\n if (range < 12 * 3600) {\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n }\n\n // For 12h-3days ranges\n if (range < 3 * 86400) {\n // For 6h+ increments, align to nice hour boundaries\n if (increment >= 6 * 3600 * 1000) {\n const hourStep = increment / 1000 / 3600; // e.g., 6, 12, etc.\n const startDate = new Date(min * 1000);\n\n if (timeZone === \"UTC\") {\n const currentHour = startDate.getUTCHours();\n const alignedHour = Math.floor(currentHour / hourStep) * hourStep;\n startDate.setUTCHours(alignedHour, 0, 0, 0);\n } else {\n const currentHour = startDate.getHours();\n const alignedHour = Math.floor(currentHour / hourStep) * hourStep;\n startDate.setHours(alignedHour, 0, 0, 0);\n }\n\n let t = startDate.getTime() / 1000;\n if (t < min) {\n t += step;\n }\n\n while (t <= max) {\n splits.push(t);\n t += step;\n }\n return splits;\n }\n\n // For smaller increments in 12h-3day ranges\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n }\n\n // For ranges >= 3 days, align to midnight\n if (increment >= timeUnitSize.day) {\n const currentDate = new Date(min * 1000);\n\n if (timeZone === \"UTC\") {\n currentDate.setUTCHours(0, 0, 0, 0);\n if (currentDate.getTime() / 1000 < min) {\n currentDate.setUTCDate(currentDate.getUTCDate() + multiplier);\n }\n } else {\n currentDate.setHours(0, 0, 0, 0);\n if (currentDate.getTime() / 1000 < min) {\n currentDate.setDate(currentDate.getDate() + multiplier);\n }\n }\n\n let t = currentDate.getTime() / 1000;\n while (t <= max) {\n splits.push(t);\n if (timeZone === \"UTC\") {\n currentDate.setUTCDate(currentDate.getUTCDate() + multiplier);\n } else {\n currentDate.setDate(currentDate.getDate() + multiplier);\n }\n t = currentDate.getTime() / 1000;\n }\n return splits;\n }\n\n // Default: simple rounding\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n}\n\n/**\n * X-Axis: Format splits for relative time axis (only show labels at first and last)\n */\nfunction generateRelativeXAxisValues(\n u: uPlot,\n splits: number[],\n timeZone?: string,\n): string[] {\n if (splits.length === 0) return [];\n\n return splits.map((timestamp, index) => {\n // Only show labels for first and last positions\n if (index === 0 || index === splits.length - 1) {\n // Convert seconds to milliseconds for dateTimeFormatTimeAgo\n return dateTimeFormatTimeAgo(timestamp * 1000, { timeZone });\n }\n return \"\"; // Return empty string for all other positions\n });\n}\n\n/**\n * X-Axis: Format splits into display labels\n */\nfunction generateXAxisValues(\n u: uPlot,\n splits: number[],\n timeZone?: string,\n): string[] {\n const scale = u.scales.x;\n const range = ((scale?.max ?? 0) - (scale?.min ?? 0)) * 1000; // Convert to ms\n const approxTicks = Math.floor(u.width / 100);\n const targetSeconds = range / 1000 / approxTicks;\n const { increment } = findBestIncrement(targetSeconds);\n\n // Convert splits from seconds to milliseconds for formatTime\n const splitsInMs = splits.map((s) => s * 1000);\n return formatTime(splitsInMs, increment, range, timeZone);\n}\n\n/**\n * X-Axis: Create complete axis configuration\n */\nexport function createXAxisConfig(\n fontFamily: string,\n timeZone?: string,\n relativeTimeAxis: boolean = false,\n): uPlot.Axis {\n const splits: uPlot.Axis.Splits = relativeTimeAxis\n ? (u, _, min, max) => generateRelativeXAxisSplits(u, min, max)\n : (u, _, min, max) => generateXAxisSplits(u, min, max, timeZone);\n const values: uPlot.Axis.Values = relativeTimeAxis\n ? (u, splits) => generateRelativeXAxisValues(u, splits, timeZone)\n : (u, splits) => generateXAxisValues(u, splits, timeZone);\n\n return {\n font: getChartFont(fontFamily),\n labelFont: getChartFont(fontFamily),\n grid: {\n show: false,\n width: 0.5,\n },\n ticks: {\n width: 0.5,\n },\n splits,\n values,\n size: 20,\n };\n}\n","import { computePosition, flip, offset } from \"@floating-ui/dom\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport uPlot from \"uplot\";\n\n// Constants\nconst TOOLTIP_DISTANCE_CURSOR = 4;\nconst TOOLTIP_PADDING_FROM_CURSOR = 8;\n\nexport interface AnchorPosition {\n left?: number;\n top?: number;\n}\n\n/**\n * We want to make sure we have a single\n * Tooltip in the whole application.\n *\n * The tooltip manager is in charge of the tooltip lifecycle.\n */\nclass TooltipManager {\n private overlay: HTMLElement | null = null;\n private reactRoot: Root | null = null;\n private renderedUplot: uPlot | null = null;\n\n initialize() {\n if (this.overlay) return;\n\n this.overlay = document.createElement(\"div\");\n this.overlay.id = \"unblind-tooltip-overlay\";\n this.overlay.style.display = \"none\";\n this.overlay.style.position = \"fixed\";\n this.overlay.style.pointerEvents = \"none\";\n this.overlay.style.zIndex = \"9999\";\n document.body.appendChild(this.overlay);\n\n this.reactRoot = createRoot(this.overlay);\n }\n\n getOverlay(): HTMLElement | null {\n return this.overlay;\n }\n\n render(u: uPlot | null, content: React.ReactElement | null) {\n if (this.reactRoot) {\n this.reactRoot.render(content);\n this.renderedUplot = u;\n } else {\n this.renderedUplot = null;\n }\n }\n\n show() {\n if (this.overlay) {\n this.overlay.style.display = \"block\";\n }\n }\n\n hide(u: uPlot) {\n if (u !== this.renderedUplot) {\n console.warn(\"Hide call plot\");\n return;\n }\n if (this.overlay) {\n this.overlay.style.display = \"none\";\n }\n this.render(null, null);\n }\n\n getRenderedUplot(): uPlot | null {\n return this.renderedUplot;\n }\n\n /**\n * Positions the tooltip overlay relative to the cursor\n * @param anchor - Cursor position\n */\n async positionTooltip(anchor: AnchorPosition): Promise<void> {\n const overlay = this.getOverlay();\n if (overlay) {\n const { x, y } = await computePosition(\n {\n getBoundingClientRect: () => ({\n x: anchor.left!,\n y: anchor.top!,\n width: 0,\n height: 0,\n top: anchor.top!,\n left: anchor.left!,\n right: anchor.left!,\n bottom: anchor.top!,\n }),\n },\n overlay,\n {\n placement: \"top-start\",\n strategy: \"fixed\" as const,\n middleware: [\n offset({\n mainAxis: TOOLTIP_DISTANCE_CURSOR,\n crossAxis: TOOLTIP_PADDING_FROM_CURSOR,\n }),\n flip(),\n ],\n },\n );\n\n overlay.style.left = `${x}px`;\n overlay.style.top = `${y}px`;\n }\n }\n}\n\n// Singleton instance\nexport const tooltipManager = new TooltipManager();\n","import { dateTimeFormat } from \"@unblind/units\";\nimport { useMemo } from \"react\";\nimport \"./Tooltip.css\";\nimport { MetricMetadata } from \"@/types\";\nimport uPlot from \"uplot\";\n\n// Types\nexport interface TooltipItem {\n metric: MetricMetadata;\n serie: uPlot.Series;\n color: string;\n value?: number;\n formattedValue?: string;\n attributes?: Record<string, string>;\n}\n\nexport type TooltipLayout = \"auto\" | \"group-by-metric\" | \"flat\";\n/**\n * Formats for rendering attribute and metric names.\n * @example\n * nodejs.eventloop.p50\n * ```\n * original: \"nodejs.eventloop.p50\"\n * suffix: \"p50\"\n * title: \"Nodejs Eventloop P50\"\n * ```\n */\nexport type TooltipFormat = \"original\" | \"suffix\" | \"title\";\nexport interface TooltipProps {\n timestamp: number;\n items: TooltipItem[];\n timeZone?: string;\n}\n\ninterface TooltipExtendedProps extends TooltipProps {\n spansMultipleDays?: boolean;\n sortByValues: boolean;\n stacked: boolean;\n hideAttributeKey?: boolean;\n hideMetric?: boolean;\n hideAttributes?: boolean;\n layout?: TooltipLayout;\n format?: TooltipFormat;\n}\n\ninterface GroupedItems {\n label: string;\n items: TooltipItem[];\n}\n\n// Helper Functions\nfunction getMetricName(item: TooltipItem): string {\n return (\n item.metric.displayName ||\n item.metric.name ||\n (typeof item.serie.label === \"string\"\n ? item.serie.label\n : item.serie.label?.textContent || \"Unknown\")\n );\n}\n\nfunction getSortKey(item: TooltipItem): string {\n const attrs = item.attributes || {};\n if (attrs[\"service.name\"]) return String(attrs[\"service.name\"]);\n\n const keys = Object.keys(attrs);\n const firstKey = keys[0];\n return firstKey ? String(attrs[firstKey]) : \"z-fallback\";\n}\n\nfunction sortItemsByValue(items: TooltipItem[]): TooltipItem[] {\n return [...items].sort(\n (a, b) => (Number(b.value) || 0) - (Number(a.value) || 0),\n );\n}\n\nfunction sortItemsAlphabetically(items: TooltipItem[]): TooltipItem[] {\n return [...items].sort((a, b) => getSortKey(a).localeCompare(getSortKey(b)));\n}\n\nfunction groupItemsByMetric(\n items: TooltipItem[],\n): Record<string, TooltipItem[]> {\n const groups: Record<string, TooltipItem[]> = {};\n\n items.forEach((item) => {\n const metricName = getMetricName(item);\n if (!groups[metricName]) {\n groups[metricName] = [];\n }\n groups[metricName].push(item);\n });\n\n return groups;\n}\n\nfunction createGroupedItems(\n items: TooltipItem[],\n sortByValues: boolean,\n layout: TooltipLayout,\n format: TooltipFormat = \"original\",\n): GroupedItems[] {\n // For flat layout, everything in one group\n if (layout === \"flat\") {\n const sortedItems = sortByValues\n ? sortItemsByValue(items)\n : sortItemsAlphabetically(items);\n\n return [{ label: \"\", items: sortedItems }];\n }\n\n // Group-by-metric layout\n const groups = groupItemsByMetric(items);\n const sortedGroupKeys = Object.keys(groups).sort((a, b) =>\n a.localeCompare(b),\n );\n\n return sortedGroupKeys.map((key) => {\n const groupItems = groups[key];\n if (!groupItems) return { label: formatLabel(key, format), items: [] };\n\n const sortedGroupItems = sortByValues\n ? sortItemsByValue(groupItems)\n : sortItemsAlphabetically(groupItems);\n\n return { label: formatLabel(key, format), items: sortedGroupItems };\n });\n}\n\nfunction formatLabel(\n label: string,\n format: TooltipFormat,\n skipUppercase?: boolean,\n): string {\n switch (format) {\n case \"suffix\": {\n // container.cpu.time -> Time | time (if skipUppercase)\n const parts = label.split(/[._-]/);\n const last = parts[parts.length - 1] || \"\";\n\n if (skipUppercase) return last;\n\n return last.charAt(0).toUpperCase() + last.slice(1).toLowerCase();\n }\n\n case \"title\":\n // container.cpu.time -> Container Cpu Time\n // or: container cpu time (if skipUppercase)\n return label\n .split(/[._-]/)\n .map((word) =>\n skipUppercase\n ? word\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n )\n .join(\" \");\n\n case \"original\":\n default:\n return label;\n }\n}\n\n/**\n * Picks the tooltip layout.\n *\n * - Returns an explicit layout if provided (except \"auto\").\n * - When using \"auto\", groups by metric if items have attributes.\n * - Otherwise, defaults to a flat layout.\n */\nfunction determineLayout(\n items: TooltipItem[],\n explicitLayout?: TooltipLayout,\n): TooltipLayout {\n if (explicitLayout && explicitLayout !== \"auto\") {\n return explicitLayout;\n }\n\n // Auto-detect logic\n const hasAttributes = items.some(\n (item) => item.attributes && Object.keys(item.attributes).length > 0,\n );\n const uniqueMetrics = new Set(items.map(getMetricName));\n if (hasAttributes && uniqueMetrics.size >= 1) {\n return \"group-by-metric\";\n }\n\n return \"flat\";\n}\n\nfunction TooltipItemRowAttributes({\n hideAttributeKey,\n attribute,\n format,\n isLastAttribute,\n}: {\n hideAttributeKey: boolean;\n isLastAttribute: boolean;\n attribute: [string, string];\n format: TooltipFormat;\n}) {\n const [key, value] = attribute;\n\n return (\n <div className=\"ub-tooltip-item-attribute-container\">\n <div\n className={\n \"ub-truncate\" +\n (hideAttributeKey\n ? \" ub-tooltip-item-hidden-attribute-key\"\n : \" ub-tooltip-item-attribute-with-key\")\n }\n >\n {!hideAttributeKey && (\n <span className=\"ub-tooltip-item-attribute-key\">\n {formatLabel(key, format)}\n {\":\"}\n </span>\n )}\n <span className=\"ub-tooltip-item-attribute-value ub-truncate\">\n {value}\n </span>\n </div>\n {!isLastAttribute && hideAttributeKey && (\n <span\n data-text=\", \"\n className=\"ub-tooltip-item-attribute-divider\"\n ></span>\n )}\n </div>\n );\n}\n\n/**\n * Has the two possible following structures:\n *\n * 1. {{metricName}} {{value}}\n * 2. {{metricName}} {{attributes}} {{value}}\n * 3. {{attributes}} {{value}}\n *\n **/\nfunction TooltipItemRow({\n item,\n hideMetric,\n hideAttributeKey,\n hideAttributes,\n format,\n}: {\n item: TooltipItem;\n hideMetric: boolean;\n hideAttributeKey: boolean;\n hideAttributes: boolean;\n format: TooltipFormat;\n}) {\n const entries = Object.entries(item.attributes || {});\n const attributes: [string, string][] = entries;\n const showAttributes = attributes.length > 0 && !hideAttributes;\n\n if (hideAttributes && hideMetric) {\n console.warn(\"Invalid configuration. Attributes and metrics are hidden.\");\n }\n\n return (\n <div className=\"ub-tooltip-item-row\">\n <div\n className={\n \"ub-tooltip-item-left\" +\n (hideMetric ? \" ub-tooltip-item-hidden-metric\" : \"\")\n }\n >\n <div className=\"ub-tooltip-item-heading\">\n <span\n className=\"ub-tooltip-item-dot\"\n style={{ backgroundColor: item.color }}\n />\n {!hideMetric ? (\n <span\n className={\n \"ub-truncate\" +\n (showAttributes\n ? \" ub-tooltip-item-metric-name-label\"\n : \" ub-tooltip-item-metric-name\")\n }\n >\n {formatLabel(item.metric.name, format)}\n </span>\n ) : (\n <div className=\"ub-tooltip-item-attributes\">\n {attributes.map((attribute, i) => (\n <TooltipItemRowAttributes\n key={\"attr_\" + attribute[0]}\n hideAttributeKey={hideAttributeKey}\n attribute={attribute}\n format={format}\n isLastAttribute={i >= attributes.length - 1}\n />\n ))}\n </div>\n )}\n </div>\n {showAttributes && !hideMetric && (\n <div className=\"ub-tooltip-item-attributes\">\n {attributes.map((attribute, i) => (\n <TooltipItemRowAttributes\n key={\"attr_\" + attribute[0]}\n hideAttributeKey={hideAttributeKey}\n attribute={attribute}\n format={format}\n isLastAttribute={i >= attributes.length - 1}\n />\n ))}\n </div>\n )}\n </div>\n\n <div className=\"ub-tooltip-item-value ub-truncate\">\n {item.formattedValue ?? (\n <span className=\"ub-tooltip-item-value--empty\">—</span>\n )}\n </div>\n </div>\n );\n}\n\nfunction Divider({\n className = \"ub-tooltip-divider\",\n ...props\n}: React.ComponentPropsWithoutRef<\"hr\">) {\n return <hr role=\"presentation\" {...props} className={className} />;\n}\n\n// Tooltip Group Component\nfunction TooltipGroup({\n group,\n groupIdx,\n hideAttributeKey,\n hideMetric,\n hideAttributes,\n format,\n hideGroupLabel,\n}: {\n group: GroupedItems;\n groupIdx: number;\n hideAttributeKey: boolean;\n hideAttributes: boolean;\n hideMetric: boolean;\n hideGroupLabel: boolean;\n format: TooltipFormat;\n}) {\n return (\n <div key={group.label} className=\"ub-tooltip-group\">\n {!hideGroupLabel && (\n <div\n className={`ub-tooltip-group-header ub-truncate${\n groupIdx > 0 ? \" ub-tooltip-group-header--separator\" : \"\"\n }`}\n >\n {group.label}\n </div>\n )}\n\n <div className=\"ub-tooltip-items\">\n {group.items.map((item, idx) => (\n <TooltipItemRow\n key={`item-${groupIdx}-${idx}`}\n item={item}\n format={format}\n hideAttributeKey={hideAttributeKey}\n hideMetric={hideMetric}\n hideAttributes={hideAttributes}\n />\n ))}\n </div>\n </div>\n );\n}\n\n// Tooltip Component\n//\n// Tooltip scenarios are a bit complex,\n// but we have the following variables:\n//\n// 1. A single metric with no attributes\n// 2. A single metric and multiple attribute sets\n// 3. A single metric and a single attribute set\n// 4. Multiple metrics\n// 5. Multiple metrics and a single attribute set\n// 6. Multiple metrics and multiple attribute sets\n//\n// Also:\n// a. Displaying attributes is optional\n// b. Displaying attributes keys is optional\n// c. If attributes are present, and there is only one metric,\n// displaying metric name is optional\n//\n// Possible layouts:\n// a. Flat\n// b. Groupped by attributes or metrics.\n//\n// So we end up with the following scenarios:\n//\n// Ref. {{attributes}} = [{{attribute.key}}: {{attribute.value}}, ..{{...}}]\n//\n// 1. A single metric with no attributes (metric hidding is not possible):\n// a. {{metricName}} {{value}}\n//\n// 2. A single metric and multiple attributes:\n// a. {{metricName}} {{attributes}} {{value}}\n// b. {{attributes}} {{value}}\n// c. {{metricName}} {{value}}\n// d. {{metricName}} (groupped)\n// {{attributes}} {{value}}\n// e. {{attributes}} (groupped)\n// {{metricName}} {{value}}\n//\n// 3. A single metric and a single attribute:\n// a. {{metricName}} {{attribute}} {{value}}\n// b. {{attribute}} {{value}}\n// c. {{metricName}} {{value}}\n//\n// 4. Multiple metrics (metric hidding is not possible):\n// a. {{metricName}} {{value}}\n//\n// 5. Multiple metrics and a single attribute\n// a. {{metricName}} {{attribute}} {{value}}\n// {{metricName}} {{attribute}} {{value}}\n// b. {{attribute}} {{value}} (not possible)\n// c. {{metricName}} {{value}}\n// {{metricName}} {{value}}\n// d. {{attribute}} (groupped)\n// {{metricName}} {{value}}\n//\n// 6. Multiple metrics and multiple attributes\n// a. {{metricName}} {{attributes}} {{value}}\n// {{metricName}} {{attributes}} {{value}}\n// b. {{metricName}} {{value}} (hide attributes)\n// {{metricName}} {{value}}\n// c. {{metricName}} (groupped)\n// {{attributes}} {{value}}\n// {{metricName}} (groupped)\n// {{attributes}} {{value}}\n// d. {{attributes}} (groupped)\n// {{metricName}} {{value}}\n// {{attributes}} (groupped)\n// {{metricName}} {{value}}\n// e. {{attributes}} {{value}} (not possible)\n//\n// The following scenarios shouldn't be possible:\n// a. One serie with attributes, and another with none.\n// b. Empty or null attributes or metric names.\n// c. Duplicated metric + attribute set combination.\nexport function Tooltip({\n timestamp,\n items,\n timeZone,\n spansMultipleDays,\n sortByValues,\n format = \"original\",\n layout: propsLayout = \"flat\",\n hideAttributeKey: propsHideAttributeKey,\n hideMetric: propsHideMetric,\n hideAttributes: propsHideAttributes,\n}: TooltipExtendedProps) {\n const formattedTime = spansMultipleDays\n ? dateTimeFormat(timestamp * 1000, {\n format: \"MMM DD, HH:mm\",\n timeZone,\n })\n : dateTimeFormat(timestamp * 1000, {\n format: \"HH:mm\",\n timeZone,\n });\n\n const layout = useMemo(\n () => determineLayout(items, propsLayout),\n [items, propsLayout],\n );\n const hideGroupLabel = layout === \"flat\";\n const isGroupByMetric = layout === \"group-by-metric\";\n\n // Logic: Hide metric name in row if explicit prop says so,\n // OR if we are already grouping by metric (redundant),\n // OR if there is only 1 metric total (redundant).\n const shouldHideMetric =\n typeof propsHideMetric === \"boolean\" ? propsHideMetric : isGroupByMetric;\n const hideAttributeKey =\n typeof propsHideAttributeKey === \"boolean\" ? propsHideAttributeKey : false;\n const hideAttributes =\n typeof propsHideAttributes === \"boolean\" ? propsHideAttributes : false;\n\n const groupedItems = useMemo(() => {\n return createGroupedItems(items, sortByValues, layout, format);\n }, [items, sortByValues, layout, format]);\n\n return (\n <div className=\"ub-tooltip\">\n <div className=\"ub-tooltip-datetime\">{formattedTime}</div>\n\n <Divider />\n\n {groupedItems.length > 0 ? (\n <div className=\"ub-tooltip-content\">\n {groupedItems.map((group, groupIdx) => (\n <TooltipGroup\n key={group.label}\n group={group}\n groupIdx={groupIdx}\n hideAttributeKey={hideAttributeKey}\n hideMetric={shouldHideMetric}\n hideAttributes={hideAttributes}\n hideGroupLabel={hideGroupLabel}\n format={format}\n />\n ))}\n </div>\n ) : (\n <div className=\"ub-tooltip-no-data\">No data available</div>\n )}\n </div>\n );\n}\n","import uPlot from \"uplot\";\nimport type { MetricMetadata, Serie } from \"../../types\";\nimport { tooltipManager } from \"./TooltipManager\";\nimport {\n Tooltip,\n TooltipFormat,\n TooltipItem,\n TooltipLayout,\n TooltipProps,\n} from \".\";\n\n/**\n * Checks if the data spans multiple calendar days\n * @param xData - Array of timestamps\n * @param timeZone - Timezone string (e.g., \"UTC\", \"America/New_York\")\n * @returns True if data spans multiple days\n */\nfunction checkSpansMultipleDays(\n xData: uPlot.TypedArray | number[],\n timeZone?: string,\n): boolean {\n if (!xData || xData.length === 0) return false;\n\n const minTimestamp = xData[0];\n const maxTimestamp = xData[xData.length - 1];\n\n if (minTimestamp == null || maxTimestamp == null) return false;\n\n const minDate = new Date(minTimestamp * 1000);\n const maxDate = new Date(maxTimestamp * 1000);\n\n const getDateString = (date: Date) => {\n if (timeZone === \"UTC\") {\n return `${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()}`;\n }\n return date.toLocaleDateString(undefined, { timeZone });\n };\n\n return getDateString(minDate) !== getDateString(maxDate);\n}\n\n/**\n * Finds the nearest data point with a non-null value\n * @param u - uPlot instance\n * @param idx - Current cursor index\n * @returns Index of nearest non-null data point, or the original index\n */\nfunction findNearestNonNullIndex(u: uPlot, idx: number): number {\n let hasNonNullValue = false;\n\n // Check if current index has any non-null values\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[idx] != null) {\n hasNonNullValue = true;\n break;\n }\n }\n\n if (hasNonNullValue) return idx;\n\n // Search for nearest non-null value\n const dataLength = u.data[0].length;\n\n for (\n let distance = 1;\n idx + distance < dataLength || idx - distance >= 0;\n distance++\n ) {\n const leftCandidate = idx - distance;\n const rightCandidate = idx + distance;\n\n if (leftCandidate >= 0) {\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[leftCandidate] != null) {\n return leftCandidate;\n }\n }\n }\n\n if (rightCandidate < dataLength) {\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[rightCandidate] != null) {\n return rightCandidate;\n }\n }\n }\n }\n\n return idx;\n}\n\n/**\n * Builds tooltip items from uPlot data\n */\nfunction buildTooltipItems(\n u: uPlot,\n actualIdx: number,\n formatValue: (v: number) => string,\n stacked: boolean,\n metadata: Record<string, MetricMetadata>,\n series?: Serie[],\n): { items: TooltipItem[]; hasAttributes: boolean } {\n let hasAttributes = false;\n const items: TooltipItem[] = [];\n\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n let rawVal = (u.data[seriesIndex]?.[actualIdx] ?? null) as number | null;\n\n if (stacked && rawVal != null && seriesIndex > 1) {\n const prevVal = u.data[seriesIndex - 1]?.[actualIdx] ?? 0;\n rawVal = rawVal - prevVal;\n }\n\n const s = u.series[seriesIndex] as uPlot.Series;\n const originalSerie = series?.[seriesIndex - 1];\n if (!originalSerie) {\n console.warn(\"Original serie not found\");\n continue;\n }\n const metric = metadata[originalSerie.metric];\n if (!metric) {\n console.warn(\"Metric metadata not found\");\n continue;\n }\n\n const stroke = s?.stroke;\n const color: string =\n typeof stroke === \"function\"\n ? (stroke(u, seriesIndex) as string)\n : ((stroke as string) ?? \"#ffffff00\");\n const formattedValue =\n rawVal == null\n ? undefined\n : formatValue\n ? formatValue(rawVal)\n : String(rawVal);\n\n if (\n originalSerie?.attributes &&\n Object.keys(originalSerie?.attributes).length > 0\n ) {\n hasAttributes = true;\n }\n\n items.push({\n metric,\n color,\n value: rawVal === null ? undefined : rawVal,\n formattedValue,\n attributes: originalSerie?.attributes,\n serie: s,\n });\n }\n\n return { items, hasAttributes };\n}\n\n/**\n * uPlot plugin for interactive tooltips\n * Extracted from: https://github.com/leeoniya/uPlot/blob/master/demos/cursor-tooltip.html\n */\nexport function tooltipPlugin(\n formatValue: (v: number) => string,\n stacked: boolean,\n metadata: Record<string, MetricMetadata>,\n sortByValues: boolean,\n timeZone?: string,\n appearance?: React.ComponentType<TooltipProps>,\n series?: Serie[],\n layout?: TooltipLayout,\n format?: TooltipFormat,\n hideAttributeKey?: boolean,\n hideAttributes?: boolean,\n hideMetric?: boolean,\n) {\n let over: HTMLElement;\n let boundingLeft: number;\n let boundingTop: number;\n let isHovering = false;\n\n function syncBounds(): void {\n const bbox = over.getBoundingClientRect();\n boundingLeft = bbox.left;\n boundingTop = bbox.top;\n }\n\n return {\n hooks: {\n init: (u: uPlot) => {\n tooltipManager.initialize();\n over = u.over;\n\n window.addEventListener(\"scroll\", syncBounds, true);\n window.addEventListener(\"resize\", syncBounds);\n\n over.onmouseenter = () => {\n isHovering = true;\n tooltipManager.show();\n };\n\n over.onmouseleave = () => {\n isHovering = false;\n\n if (tooltipManager.getRenderedUplot() === u) {\n tooltipManager.hide(u);\n }\n };\n\n syncBounds();\n },\n\n setSize: () => {\n syncBounds();\n },\n\n setCursor: (u: uPlot) => {\n const { left, top, idx } = u.cursor;\n const shouldHideTooltip = !isHovering || idx == null;\n\n if (shouldHideTooltip) {\n if (tooltipManager.getRenderedUplot() === u) {\n tooltipManager.hide(u);\n }\n return;\n }\n\n const xData = u.data[0];\n const spansMultipleDays = checkSpansMultipleDays(xData, timeZone);\n const actualIdx = findNearestNonNullIndex(u, idx);\n const timestamp = u.data[0][actualIdx];\n\n if (timestamp === undefined) {\n return;\n }\n\n const { items } = buildTooltipItems(\n u,\n actualIdx,\n formatValue,\n stacked,\n metadata,\n series,\n );\n\n tooltipManager.show();\n\n const AppearanceTooltip = appearance;\n const tooltipElement = AppearanceTooltip ? (\n <AppearanceTooltip\n timestamp={timestamp}\n items={items}\n timeZone={timeZone}\n />\n ) : (\n <Tooltip\n timestamp={timestamp}\n items={items}\n timeZone={timeZone}\n spansMultipleDays={spansMultipleDays}\n stacked={stacked}\n sortByValues={sortByValues}\n layout={layout}\n format={format}\n hideAttributeKey={hideAttributeKey}\n hideAttributes={hideAttributes}\n hideMetric={hideMetric}\n />\n );\n\n tooltipManager.render(u, tooltipElement);\n tooltipManager.positionTooltip({\n left: (left || 0) + boundingLeft,\n top: (top || 0) + boundingTop,\n });\n },\n\n destroy(u: uPlot) {\n window.removeEventListener(\"scroll\", syncBounds, true);\n window.removeEventListener(\"resize\", syncBounds);\n\n if (over) {\n over.onmouseenter = null;\n over.onmouseleave = null;\n }\n\n if (tooltipManager.getRenderedUplot() === u) {\n tooltipManager.hide(u);\n }\n },\n },\n };\n}\n","import uPlot from \"uplot\";\nimport { getChartFont } from \"./utils\";\n\nexport const calculateNiceStep = (maxMin: number, maxTicks: number = 4) => {\n // Determine step size based on min/max value (targeting ~4-5 ticks)\n const roughStep = maxMin / maxTicks;\n\n // Round to \"nice\" numbers (1, 2, 5) at any magnitude\n const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));\n const residual = roughStep / magnitude;\n\n let niceStep;\n if (residual > 5) {\n niceStep = 10 * magnitude;\n } else if (residual > 2) {\n niceStep = 5 * magnitude;\n } else if (residual > 1) {\n niceStep = 2 * magnitude;\n } else {\n niceStep = magnitude;\n }\n const niceVal = Math.ceil(maxMin / niceStep) * niceStep;\n\n return niceVal;\n};\n\nconst usePredefinedIfExists = (\n predefinedNumber: number | undefined,\n defaultNumber: number,\n) => {\n return typeof predefinedNumber === \"number\"\n ? predefinedNumber\n : defaultNumber;\n};\n\nexport const buildScaleRange: (\n unit: string | undefined,\n predefinedMin?: number | undefined,\n predefinedMax?: number | undefined,\n) => uPlot.Scale.Range = (\n unit: string | undefined,\n predefinedMin?: number | undefined,\n predefinedMax?: number | undefined,\n) => {\n return (_: uPlot, dataMin: number, dataMax: number) => {\n if (dataMin === 0 && dataMax === 0) {\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, 100),\n ];\n }\n // Add a bit of padding at the top\n const padding = 1;\n const max = dataMax * (1 + padding);\n const min = dataMin < 0 ? dataMin * (1 + padding) : dataMin * (1 - padding);\n\n if (unit === \"percent\" || unit === \"percentunit\") {\n if (dataMax > 100) {\n if (dataMin < 0) {\n return [\n usePredefinedIfExists(predefinedMin, min),\n usePredefinedIfExists(predefinedMax, max),\n ];\n }\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, max),\n ];\n } else if (dataMin < 0) {\n return [\n usePredefinedIfExists(predefinedMin, min),\n usePredefinedIfExists(predefinedMax, 100),\n ];\n }\n\n // Normal case\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, 100),\n ];\n }\n\n // For edge case where max is 0 or very close to 0\n if (dataMax <= 0.9999) {\n if (dataMin <= 0) {\n return [\n usePredefinedIfExists(predefinedMin, min),\n usePredefinedIfExists(predefinedMax, 1),\n ];\n }\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, 1),\n ];\n }\n\n const niceMax = calculateNiceStep(max);\n const niceMin = calculateNiceStep(Math.abs(min));\n\n // Important to not do <= 0, otherwise will break some edge cases where dataMin = 0\n if (dataMin < 0) {\n return [\n usePredefinedIfExists(predefinedMax, -niceMin),\n usePredefinedIfExists(predefinedMax, niceMax),\n ];\n }\n\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, niceMax),\n ];\n };\n};\n\n/**\n * Y-Axis: Calculate axis size based on longest value label\n */\nconst calculateYAxisSize = (\n self: uPlot,\n values: Array<string>,\n axisIdx: number,\n cycleNum: number,\n): number => {\n const axis = self.axes[axisIdx];\n\n // bail out, force convergence\n if (cycleNum > 1)\n // Do not use .size, it doesn't work. Use ._size instead.\n return (axis as { _size: number })?._size || 0;\n\n let axisSize = (axis?.ticks?.size || 0) + (axis?.gap || 0);\n\n // find longest value\n const longestVal = (values ?? []).reduce(\n (acc, val) => (val.length > acc.length ? val : acc),\n \"\",\n );\n\n if (longestVal != \"\") {\n self.ctx.font = axis?.font?.[0] ?? self.ctx.font;\n axisSize += self.ctx.measureText(longestVal).width / devicePixelRatio;\n }\n\n return Math.ceil(axisSize);\n};\n\n/**\n * Y-Axis: Create complete axis configuration\n */\nexport function createYAxisConfig(\n formatter: (\n v: number,\n decimals?: number,\n ) => { text: string; suffix?: string },\n fontFamiliy: string,\n): uPlot.Axis {\n return {\n gap: 0,\n font: getChartFont(fontFamiliy),\n labelFont: getChartFont(fontFamiliy),\n grid: {\n show: true,\n width: 0.5,\n },\n ticks: {\n width: 0.5,\n },\n values: (_, vals) =>\n vals.map((v) => {\n const formmatedVal = formatter(v);\n return formmatedVal.text + (formmatedVal.suffix?.trim() || \"\");\n }),\n size: calculateYAxisSize,\n space: 60,\n };\n}\n\n/**\n * Stacking data for bar charts\n * Extracted and mutated from: https://github.com/leeoniya/uPlot/blob/master/demos/stack.js\n */\nexport function stack(\n data: uPlot.AlignedData,\n omit: boolean,\n): {\n data: uPlot.AlignedData;\n bands: Array<uPlot.Band>;\n} {\n const bands: Array<uPlot.Band> = [];\n const xAxis = data[0];\n const xAxisLen = xAxis.length;\n const accum = Array(xAxisLen).fill(0);\n const accumData: uPlot.AlignedData = [xAxis];\n\n data.forEach((serie, i) => {\n // Skip x-axis\n if (i === 0) return;\n if (omit) {\n accumData.push(serie);\n } else {\n accumData.push(\n serie.map(\n (value, index) => (accum[index] = accum[index] + (value || 0)),\n ),\n );\n }\n });\n\n data.forEach((_, serieIndex) => {\n if (serieIndex === 0 || omit) return;\n bands.push({\n series: [data.findIndex((_, j) => j > serieIndex), serieIndex],\n });\n });\n\n return {\n data: accumData,\n bands: bands.filter((b) => b.series[1] > -1),\n };\n}\n"],"mappings":"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","names":["QueryClient","QueryClientProvider","useQueryClient","createContext","useCallback","useContext","useMemo","jsx","UnblindClientConfigContext","UnblindClientProvider","children","providedQueryClient","providedQueryClientConfig","apiBaseUrl","fetchImpl","queryClient","defaultQueryConfig","baseConfig","configValue","useUnblindClientConfig","ctx","useRefresh","createContext","useContext","useMemo","jsx","jsxs","Empty","Error","Loading","jsx","ScopeConfigContext","createContext","Scope","children","timeRange","startTime","endTime","interval","attributes","groupBy","operator","appearance","tooltip","colors","fill","sortByValues","parentContext","useContext","LoadingComponent","ErrorComponent","TooltipComponent","EmptyComponent","tooltipLayout","tooltipFormat","tooltipHideAttributes","tooltipHideAttributeKey","tooltipHideMetric","memoizedAppearance","useMemo","memoizedTooltip","scopeConfigValue","DEFAULT_TIMERANGE","useScope","ctx","Loading","Error","Empty","jsx","UnblindProvider","children","queryClient","apiBaseUrl","fetchImpl","timeRange","startTime","endTime","interval","attributes","groupBy","operator","appearance","tooltip","colors","sortByValues","UnblindClientProvider","Scope","useQuery","useMetrics","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","useQuery","res","metrics","useQuery","useMemo","ms","timeRangeToCalculatedTimestamp","timeRange","now","deduceTimestamp","startTime","endTime","calculatedStartTime","calculatedEndTime","useTimeseries","queries","timeRange","startTime","endTime","interval","apiBaseUrl","fetchImpl","useUnblindClientConfig","metricNames","useMemo","x","attributes","attrs","attributeKeys","key","operators","groupBy","query","useQuery","calculatedStartTime","calculatedEndTime","deduceTimestamp","apiRequestBody","res","series","times","metadata","isLoading","isFetching","hasError","useQuery","useMemo","useUsage","timeRange","startTime","endTime","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","useQuery","calculatedStartTime","calculatedEndTime","deduceTimestamp","endpoint","res","usage","useMemo","isLoading","hasError","useInfiniteQuery","useMemo","useLogs","timeRange","filters","startTime","endTime","apiBaseUrl","fetchImpl","useUnblindClientConfig","hasValidTimeConfig","query","useInfiniteQuery","x","pageParam","filtersByName","acc","filter","body","severity","service","traceId","spanId","attributes","deducedStartTime","deducedEndTime","deduceTimestamp","res","data","next_page","lastPage","useMemo","page","useMemo","useEffect","useRef","uPlot","isLineThreshold","threshold","isRangeThreshold","uPlot","getValueFormat","dateTimeFormat","dateTimeFormatTimeAgo","systemDateFormats","timeUnitSize","timeIncrs","formatTime","splits","foundIncr","range","timeZone","format","systemDateFormats","yearRoundedToDay","v","dateTimeFormat","date","showSeconds","showMillis","checkTimeStr","findBestIncrement","targetSeconds","scales","scale","mult","incr","lastScale","lastMult","generateRelativeXAxisSplits","u","min","max","generateXAxisSplits","px","approxTicks","increment","multiplier","step","t","hourStep","startDate","currentHour","alignedHour","currentDate","generateRelativeXAxisValues","timestamp","index","dateTimeFormatTimeAgo","generateXAxisValues","splitsInMs","s","createXAxisConfig","fontFamily","relativeTimeAxis","_","values","getChartFont","computePosition","flip","offset","createRoot","TOOLTIP_DISTANCE_CURSOR","TOOLTIP_PADDING_FROM_CURSOR","TooltipManager","u","content","anchor","overlay","x","y","tooltipManager","dateTimeFormat","useMemo","jsx","jsxs","getMetricName","item","getSortKey","attrs","firstKey","sortItemsByValue","items","a","b","sortItemsAlphabetically","groupItemsByMetric","groups","metricName","createGroupedItems","sortByValues","layout","format","key","groupItems","formatLabel","sortedGroupItems","label","skipUppercase","parts","last","word","determineLayout","explicitLayout","hasAttributes","uniqueMetrics","TooltipItemRowAttributes","hideAttributeKey","attribute","isLastAttribute","value","TooltipItemRow","hideMetric","hideAttributes","attributes","showAttributes","i","Divider","className","props","TooltipGroup","group","groupIdx","hideGroupLabel","idx","Tooltip","timestamp","timeZone","spansMultipleDays","propsLayout","propsHideAttributeKey","propsHideMetric","propsHideAttributes","formattedTime","dateTimeFormat","useMemo","shouldHideMetric","groupedItems","jsx","checkSpansMultipleDays","xData","timeZone","minTimestamp","maxTimestamp","minDate","maxDate","getDateString","date","findNearestNonNullIndex","u","idx","hasNonNullValue","seriesIndex","dataLength","distance","leftCandidate","rightCandidate","buildTooltipItems","actualIdx","formatValue","stacked","metadata","series","hasAttributes","items","rawVal","prevVal","s","originalSerie","metric","stroke","color","formattedValue","tooltipPlugin","sortByValues","appearance","layout","format","hideAttributeKey","hideAttributes","hideMetric","over","boundingLeft","boundingTop","isHovering","syncBounds","bbox","tooltipManager","left","top","spansMultipleDays","timestamp","AppearanceTooltip","tooltipElement","Tooltip","DEFAULT_COLORS","getChartFont","fontFamiliy","compareAndResolveUnit","paramsUnit","metricMetadata","unit","isColorsObject","colors","isColorsArray","getColor","serie","index","colors","type","computedStyle","isFilling","isColorsArray","isColorsObject","cssColorVar","cssColor","DEFAULT_COLORS","generatePath","barsPath","uPlot","linearPath","splinePath","steppedPath","generateFill","getStrokeWidthByType","fill","cursorMovement","u","left","top","xVal","xData","nearestIdx","lo","hi","mid","midVal","loVal","hiVal","snapIdx","i","hasValue","j","seriesData","snapVal","mutateAxisGridAndTextColor","opts","textColor","gridColor","xAxis","yAxis","getThresholdColor","level","getThresholdRangeFillColor","getThresholdWidth","lineType","getThresholdDash","createThresholds","thresholds","thresholdSeries","threshold","thresholdSerie","isLineThreshold","createChartBands","series","stackedData","x","isRangeThreshold","rangeThresholdBands","idx","band","createChartSeries","stacked","strokeWidth","parseUnit","unit","sUnit","createChartOptions","container","metadata","sortByValues","timeZone","tooltipComponent","relativeTimeAxis","format","layout","hideAttributeKey","hideAttributes","hideMetric","predefinedMin","predefinedMax","fontFamily","initialWidth","initialHeight","formatUnit","formatter","getValueFormat","formattingFunction","v","f","buildScaleRange","tooltipPlugin","createXAxisConfig","createYAxisConfig","calculateNiceStep","maxMin","maxTicks","roughStep","magnitude","residual","niceStep","usePredefinedIfExists","predefinedNumber","defaultNumber","buildScaleRange","unit","predefinedMin","predefinedMax","_","dataMin","dataMax","padding","max","min","niceMax","niceMin","calculateYAxisSize","self","values","axisIdx","cycleNum","axis","axisSize","longestVal","acc","val","createYAxisConfig","formatter","fontFamiliy","getChartFont","vals","v","formmatedVal","stack","data","omit","bands","xAxis","xAxisLen","accum","accumData","serie","i","value","index","serieIndex","j","b","jsx","Chart","props","times","series","metadata","type","className","timeZone","propsOptions","tooltip","propsColors","tooltipComponent","sortByValues","propsUnit","fill","thresholds","predefinedMin","predefinedMax","tooltipLayout","tooltipFormat","hideAttributeKey","hideAttributes","hideMetric","chartRef","useRef","scope","useScope","colors","useEffect","data","unit","serie","metricMetadata","compareAndResolveUnit","threshold","isLineThreshold","isRangeThreshold","stacked","stackedData","stack","container","u","opts","createChartOptions","uPlot","resizeObserver","jsx","Timeseries","metrics","propOperator","propAttributes","propGroupBy","propInterval","type","className","propAppearance","propUnit","propThresholds","propMin","propMax","propColors","propTooltip","propSortByValues","propFill","scope","useScope","interval","attributes","groupBy","operator","colors","sortByValues","fill","tooltipComponent","tooltip","useMemo","isLoading","data","hasError","useTimeseries","metric","series","times","metadata","isEmpty","x","containerClassName","LoadingComponent","ErrorComponent","EmptyComponent","Chart"]}