@unblind/react 0.1.0-alpha.20 → 0.1.0-alpha.22

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/useTimeConfig.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 hideAxis,\n hideCursor,\n relativeTimeAxis,\n invertSort,\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 tooltipHide =\n typeof tooltip?.hide === \"boolean\"\n ? tooltip?.hide\n : parentContext?.tooltip?.hide;\n const tooltipVisibilityLimit =\n typeof tooltip?.visibilityLimit === \"number\"\n ? tooltip?.visibilityLimit\n : parentContext?.tooltip?.visibilityLimit;\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 hide: tooltipHide,\n visibilityLimit: tooltipVisibilityLimit,\n };\n }, [tooltipHide, tooltipVisibilityLimit]);\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 relativeTimeAxis:\n typeof relativeTimeAxis === \"boolean\"\n ? relativeTimeAxis\n : parentContext?.relativeTimeAxis,\n fill: typeof fill === \"boolean\" ? fill : parentContext?.fill,\n hideAxis:\n typeof hideAxis === \"boolean\" ? hideAxis : parentContext?.hideAxis,\n hideCursor:\n typeof hideCursor === \"boolean\"\n ? hideCursor\n : parentContext?.hideCursor,\n invertSort:\n typeof invertSort === \"boolean\"\n ? invertSort\n : parentContext?.invertSort,\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 fill,\n relativeTimeAxis,\n hideAxis,\n hideCursor,\n memoizedAppearance,\n memoizedTooltip,\n parentContext,\n invertSort,\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","export 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-default-error\" />;\n}\n\nexport function Loading() {\n return <div className=\"ub-default-loading\" 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 fill,\n hideAxis,\n hideCursor,\n relativeTimeAxis,\n invertSort,\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 fill={fill}\n hideAxis={hideAxis}\n hideCursor={hideCursor}\n relativeTimeAxis={relativeTimeAxis}\n invertSort={invertSort}\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 { useTimeConfig } from \"@/hooks/useTimeConfig\";\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 UseTimeseriesData {\n series: Serie[];\n times: number[];\n metadata: Record<string, MetricMetadata>;\n}\n\nexport interface UseTimeseriesReturn {\n data: UseTimeseriesData;\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 params: UseTimeseriesParams,\n): UseTimeseriesReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const { startTime, endTime, timeRange } = useTimeConfig(params);\n const { queries, interval } = params;\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 hasValidTimeConfig({\n timeRange,\n startTime,\n endTime,\n}: {\n timeRange?: TimeRange;\n startTime?: number;\n endTime?: number;\n}) {\n return (\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange\n );\n}\n\nexport function getTimeConfig({\n scope,\n props,\n}: {\n scope: {\n timeRange: TimeRange;\n startTime?: number;\n endTime?: number;\n };\n props: {\n timeRange?: TimeRange;\n startTime?: number;\n endTime?: number;\n };\n}) {\n if (props.timeRange || (props.startTime && props.endTime)) {\n return props;\n } else {\n return scope;\n }\n}\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 { getTimeConfig } from \"./utils\";\nimport { useScope } from \"@/providers\";\nimport { useMemo } from \"react\";\nimport ms from \"ms\";\n\nexport function useTimeConfig(props: {\n startTime?: number;\n endTime?: number;\n timeRange?: ms.StringValue;\n}) {\n const scope = useScope();\n\n const { timeRange, startTime, endTime } = getTimeConfig({\n props,\n scope,\n });\n\n return useMemo(\n () => ({\n timeRange,\n startTime,\n endTime,\n }),\n [timeRange, startTime, endTime],\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport { TimeConfig } from \"@/types\";\nimport { useTimeConfig } from \"@/hooks/useTimeConfig\";\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(params: UseUsageParams): UseUsageReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const { startTime, endTime, timeRange } = useTimeConfig(params);\n\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 { useTimeConfig } from \"@/hooks/useTimeConfig\";\nimport type { Log, PaginatedResponse, TimeConfig } from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport type UseLogsParams = {\n /**\n * Array of filters to apply to the logs query.\n */\n attributes?: Record<string, Array<string>>;\n body?: Array<string>;\n severity?: Array<string>;\n traceId?: string;\n spanId?: string;\n logId?: string;\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(props: UseLogsParams): UseLogsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const { timeRange, startTime, endTime } = useTimeConfig(props);\n const { attributes, body, severity, traceId, spanId, logId } = props;\n\n const query = useInfiniteQuery<PaginatedLogsResponse>({\n queryKey: [\n \"unblind\",\n \"logs\",\n timeRange,\n startTime,\n endTime,\n body,\n severity,\n logId,\n JSON.stringify(attributes),\n ],\n queryFn: async ({ pageParam }) => {\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 attributes,\n body,\n severity,\n traceId,\n spanId,\n logId,\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, nextPage } = await res.json();\n\n if (!data) {\n throw new Error(\"data not found\");\n }\n\n return { data, nextPage } as PaginatedLogsResponse;\n },\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => lastPage.nextPage,\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, UseTimeseriesReturn } from \"../../hooks/useTimeseries\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport type {\n Appearance,\n TimeseriesQueryConfig,\n ChartVisualConfig,\n} from \"../../types\";\nimport { useEffect, 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 * 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 * Hook to get request data\n */\n onResponse?: (response: UseTimeseriesReturn) => void;\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 fill: propFill,\n hideAxis: propHideAxis,\n hideCursor: propHideCursor,\n relativeTimeAxis: propRelativeTimeAxis,\n onResponse,\n invertSort: propInvertSort,\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 relativeTimeAxis =\n typeof propRelativeTimeAxis === \"boolean\"\n ? propRelativeTimeAxis\n : scope.relativeTimeAxis;\n const colors = propColors ?? scope.colors;\n const fill = typeof propFill === \"boolean\" ? propFill : scope.fill;\n const invertSort =\n typeof propInvertSort === \"boolean\" ? propInvertSort : scope.invertSort;\n const hideAxis =\n typeof propHideAxis === \"boolean\" ? propHideAxis : scope.hideAxis;\n const hideCursor =\n typeof propHideCursor === \"boolean\" ? propHideCursor : scope.hideCursor;\n const tooltipComponent =\n propAppearance?.components?.Tooltip ?? scope.appearance.components.Tooltip;\n\n const tooltip = useMemo(() => {\n return {\n hide: propTooltip?.hide ?? scope.tooltip?.hide,\n visibilityLimit:\n propTooltip?.visibilityLimit ?? scope.tooltip?.visibilityLimit,\n };\n }, [\n propTooltip?.hide,\n scope.tooltip?.hide,\n propTooltip?.visibilityLimit,\n scope.tooltip?.visibilityLimit,\n ]);\n\n const response = 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 const { isLoading, data, hasError } = response;\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 useEffect(() => {\n if (onResponse) {\n onResponse(response);\n }\n }, [response, onResponse]);\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 relativeTimeAxis={relativeTimeAxis}\n unit={propUnit}\n fill={fill}\n tooltipComponent={tooltipComponent}\n thresholds={propThresholds}\n min={propMin}\n max={propMax}\n hideAxis={hideAxis}\n hideCursor={hideCursor}\n invertSort={invertSort}\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 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 invertSort?: boolean;\n className?: string;\n timeZone?: string;\n options?: uPlot.Options;\n tooltipComponent?: React.ComponentType<TooltipProps>;\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 unit: propsUnit,\n fill = false,\n thresholds,\n min: predefinedMin,\n max: predefinedMax,\n hideAxis,\n hideCursor,\n relativeTimeAxis,\n invertSort,\n } = props;\n const { hide: hideTooltip, visibilityLimit } = 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 const stacked = type === \"bar\" || type === \"area\";\n const stackedData = stack(data, !stacked);\n\n if (thresholds) {\n thresholds.forEach((threshold) => {\n if (isLineThreshold(threshold)) {\n stackedData.data.push(new Array(times.length).fill(threshold.value));\n } else if (isRangeThreshold(threshold)) {\n stackedData.data.push(new Array(times.length).fill(threshold.from));\n stackedData.data.push(new Array(times.length).fill(threshold.to));\n }\n });\n }\n\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 timeZone,\n tooltipComponent,\n relativeTimeAxis,\n hideTooltip,\n thresholds,\n predefinedMin,\n predefinedMax,\n hideAxis,\n hideCursor,\n visibilityLimit,\n invertSort,\n );\n\n u = new uPlot(\n { ...opts, ...propsOptions },\n stackedData.data as uPlot.AlignedData,\n container,\n );\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 relativeTimeAxis,\n colors,\n fill,\n propsUnit,\n hideTooltip,\n thresholds,\n predefinedMin,\n predefinedMax,\n propsOptions,\n hideAxis,\n hideCursor,\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 { TooltipProps } 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 * 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 traceId?: string;\n spanId?: string;\n logId?: string;\n severity: Severity;\n attributes?: Record<string, string>;\n body?: string;\n serviceName?: string;\n}\n\n/**\n * Default paginated response structure\n */\nexport type PaginatedResponse<T> = {\n data: Array<T>;\n nextPage?: 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 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 /**\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 * Optional boolean to hide both axis\n */\n hideAxis?: boolean;\n\n /**\n * Optional boolean to hide cursor\n */\n hideCursor?: boolean;\n\n /**\n * Optional boolean to display relative time axis\n *\n * e.g. a day ago ------ a few seconds ago\n */\n relativeTimeAxis?: boolean;\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 invert value order.\n * Defaults to false\n */\n invertSort?: boolean;\n}\n\nexport interface TooltipConfig {\n hide?: boolean;\n visibilityLimit?: number;\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\nexport type StackedData = {\n data: Array<uPlot.AlignedData[number]>;\n bands: Array<uPlot.Band>;\n};\n\nexport interface TooltipSerie {\n metric: MetricMetadata;\n serie: uPlot.Series;\n color: string;\n value?: number;\n formattedValue?: string;\n attributes?: Record<string, string>;\n}\n","import {\n ChartType,\n Colors,\n isLineThreshold,\n isRangeThreshold,\n MetricMetadata,\n Serie,\n StackedData,\n Threshold,\n ThresholdLevel,\n ThresholdLineType,\n} from \"@/types\";\n\nimport uPlot from \"uplot\";\nimport { getValueFormat } from \"@unblind/units\";\nimport { createXAxisConfig } from \"./XAxis\";\nimport { buildScaleRange, createYAxisConfig } from \"./YAxis\";\nimport { 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\nconst MAX_COLORS = 12;\n\nconst getFillingColor = ({\n isFilling,\n useSolid,\n index,\n}: {\n isFilling?: boolean;\n useSolid: boolean;\n index: number;\n}) => {\n const colorIndex = (index % MAX_COLORS) + 1;\n\n if (isFilling) {\n return useSolid\n ? `--ub-chart-serie-color-${colorIndex}`\n : `--ub-chart-serie-fill-color-${colorIndex}`;\n }\n\n return `--ub-chart-serie-color-${colorIndex}`;\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 const useSolid = type === \"bar\" || type === \"area\";\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 = getFillingColor({ isFilling, useSolid, index });\n const cssColor = computedStyle.getPropertyValue(cssColorVar).trim();\n\n if (cssColor) {\n return cssColor;\n }\n\n // Static fallback\n return isFilling && useSolid\n ? DEFAULT_COLORS[index % DEFAULT_COLORS.length]?.replace(\")\", \" / 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: StackedData,\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 (isLineThreshold(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 }\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: StackedData,\n unit: string | undefined,\n series: Array<Serie>,\n type: ChartType,\n stacked: boolean,\n colors: Colors | undefined,\n fill: boolean,\n timeZone?: string,\n tooltipComponent?: React.ComponentType<TooltipProps>,\n relativeTimeAxis?: boolean,\n hideTooltip?: boolean,\n thresholds?: Array<Threshold>,\n predefinedMin?: number,\n predefinedMax?: number,\n hideAxis?: boolean,\n hideCursor?: boolean,\n visibilityLimit?: number,\n invertSort?: boolean,\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);\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: hideTooltip\n ? []\n : [\n tooltipPlugin(\n formattingFunction,\n stacked,\n metadata,\n timeZone,\n tooltipComponent,\n series,\n visibilityLimit,\n invertSort,\n ),\n ],\n // [top, right, bottom, left];\n padding: relativeTimeAxis ? [8, 10, 8, 48] : [8, 10, 8, 18],\n cursor: {\n y: false,\n sync: { key: \"_\" },\n drag: {\n setScale: true,\n x: true,\n y: false,\n },\n move: cursorMovement,\n show: !hideCursor,\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, hideAxis),\n createYAxisConfig(formatter, fontFamily, hideAxis),\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 return splits.map((v) => {\n const date = new Date(v);\n const day = timeZone === \"UTC\" ? date.getUTCDate() : date.getDate();\n\n if (day === 1) {\n return date.toLocaleDateString(undefined, {\n month: \"short\",\n year: \"numeric\",\n timeZone,\n });\n } else {\n return date.toLocaleDateString(undefined, {\n day: \"numeric\",\n month: \"short\",\n timeZone,\n });\n }\n });\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 */\n// function 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 hideAxis: boolean = false,\n): uPlot.Axis {\n const splits: uPlot.Axis.Splits | undefined = relativeTimeAxis\n ? (u, _, min, max) => generateRelativeXAxisSplits(u, min, max)\n : undefined;\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 show: !hideAxis,\n align: relativeTimeAxis ? 2 : undefined,\n space: (_self, _axisIdx, _scaleMin, _scaleMax, plotDim) => {\n if (plotDim < 400) {\n return 100;\n } else if (plotDim < 800) {\n return 150;\n } else {\n return 250;\n }\n },\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 { createContext, Fragment, PropsWithChildren, useContext } from \"react\";\nimport { TooltipSerie } from \"@/types\";\n\n// Types\nexport interface TooltipProps {\n timestamp: number;\n tooltipSerieList: TooltipSerie[];\n timeZone?: string;\n stacked?: boolean;\n invertSort?: boolean;\n visibilityLimit?: number;\n}\n\ninterface TooltipExtendedProps extends TooltipProps {\n spansMultipleDays?: boolean;\n hasMultipleMetrics: boolean;\n hasMultipleAttributes: boolean;\n hasAttributes: boolean;\n}\n\nconst DEFAULT_VISIBILITY_LIMIT = 6;\n\n// Context for serie\nconst SerieContext = createContext<TooltipSerie | null>(null);\n\nfunction useTooltipSerie() {\n const context = useContext(SerieContext);\n if (!context) {\n throw new Error(\"useTooltipSerie must be used within a SerieProvider\");\n }\n return context;\n}\n\nfunction SerieProvider({\n serie,\n children,\n}: PropsWithChildren<{ serie: TooltipSerie }>) {\n return (\n <SerieContext.Provider value={serie}>{children}</SerieContext.Provider>\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\nfunction MetricsTooltip({\n formattedTime,\n unitCategory,\n tooltipSerieList,\n visibilityLimit,\n invertSort,\n}: {\n formattedTime: string;\n unitCategory: string;\n tooltipSerieList: Array<TooltipSerie>;\n visibilityLimit: number;\n invertSort?: boolean;\n}) {\n const visibleSeries = tooltipSerieList.slice(0, visibilityLimit);\n const afterLimit = tooltipSerieList.slice(visibilityLimit);\n\n return (\n <div className=\"ub-tooltip ub-tooltip-multiple-metrics\">\n <Header>\n <DateTime>{formattedTime}</DateTime>\n <div className=\"ub-tooltip-header-right\">\n <UnitCategory>{unitCategory}</UnitCategory>\n </div>\n </Header>\n <Divider />\n <Content>\n {visibleSeries.map((x) => (\n <Serie serie={x} key={x.metric.name}>\n <Color />\n <Label>\n <Metric />\n </Label>\n <Value />\n </Serie>\n ))}\n <Summary series={afterLimit} invertSort={invertSort} />\n </Content>\n </div>\n );\n}\n\nfunction MultipleAttributesTooltip({\n formattedTime,\n tooltipSerieList,\n visibilityLimit,\n invertSort,\n}: {\n formattedTime: string;\n tooltipSerieList: Array<TooltipSerie>;\n visibilityLimit: number;\n invertSort?: boolean;\n}) {\n const firstSerie = tooltipSerieList[0];\n\n const visibleSeries = tooltipSerieList.slice(0, visibilityLimit);\n const afterLimit = tooltipSerieList.slice(visibilityLimit);\n\n return (\n <div className=\"ub-tooltip ub-tooltip-multiple-attributes\">\n <Header>\n <DateTime>{formattedTime}</DateTime>\n <div className=\"ub-tooltip-header-right\">\n <span className=\"ub-tooltip-serie-metric\">\n {extractSuffix(firstSerie?.metric.name || \"\")}\n </span>\n </div>\n </Header>\n <Divider />\n <Content>\n {visibleSeries.map((x, i) => (\n <Serie serie={x} key={\"serie_\" + i}>\n <Color />\n <Label>\n <Attributes />\n </Label>\n <Value />\n </Serie>\n ))}\n <Summary series={afterLimit} invertSort={invertSort} />\n </Content>\n </div>\n );\n}\n\nfunction MultipleAttributesMultipleMetricsTooltip({\n formattedTime,\n tooltipSerieList,\n unitCategory,\n visibilityLimit,\n invertSort,\n}: {\n formattedTime: string;\n unitCategory: string;\n tooltipSerieList: Array<TooltipSerie>;\n visibilityLimit: number;\n invertSort?: boolean;\n}) {\n const visibleSeries = tooltipSerieList.slice(0, visibilityLimit);\n const afterLimit = tooltipSerieList.slice(visibilityLimit);\n\n return (\n <div className=\"ub-tooltip ub-tooltip-multiple-attributes\">\n <Header>\n <DateTime>{formattedTime}</DateTime>\n <div className=\"ub-tooltip-header-right\">\n <span className=\"ub-tooltip-unit-category\">{unitCategory}</span>\n </div>\n </Header>\n <Divider />\n <Content>\n {visibleSeries.map((x, i) => (\n <Serie serie={x} key={\"serie\" + i}>\n <Color />\n <Label>\n <Metric />\n <Attributes />\n </Label>\n <Value />\n </Serie>\n ))}\n <Summary series={afterLimit} invertSort={invertSort} />\n </Content>\n </div>\n );\n}\n\nfunction UnitCategory(props: PropsWithChildren) {\n return <span className=\"ub-tooltip-unit-category\">{props.children}</span>;\n}\n\nfunction Summary({\n series,\n invertSort,\n}: {\n series: Array<TooltipSerie>;\n invertSort?: boolean;\n}) {\n const formattedVal = series[0]?.formattedValue;\n const allZeroes = !series.some((x) => (x.value || 0) > 0);\n const allUndefined = !series.some((x) => x.value !== undefined);\n\n if (series.length > 0) {\n if (allUndefined) {\n return (\n <span className=\"ub-tooltip-summary\">\n <span>+{series.length} more with no data</span>\n </span>\n );\n }\n\n if (allZeroes) {\n return (\n <span className=\"ub-tooltip-summary\">\n <span>+{series.length} more with zero values</span>\n </span>\n );\n }\n\n return (\n <span className=\"ub-tooltip-summary\">\n <span>+{series.length} more with </span>\n <span>{`${invertSort ? \"≥\" : \"≤\"} ${formattedVal}`}</span>\n </span>\n );\n }\n return <></>;\n}\n\nfunction Serie(props: PropsWithChildren & { serie: TooltipSerie }) {\n return (\n <SerieProvider serie={props.serie}>\n <div className=\"ub-tooltip-serie\">{props.children}</div>\n </SerieProvider>\n );\n}\n\nfunction Content(props: PropsWithChildren) {\n return <div className=\"ub-tooltip-content\">{props.children}</div>;\n}\n\nfunction Header(props: PropsWithChildren) {\n return <div className=\"ub-tooltip-header\">{props.children}</div>;\n}\n\nfunction DateTime(props: PropsWithChildren) {\n return <div className=\"ub-tooltip-datetime\">{props.children}</div>;\n}\n\nfunction Metric() {\n const serie = useTooltipSerie();\n return (\n <span className=\"ub-tooltip-serie-metric\">\n {extractSuffix(serie.metric.name)}\n </span>\n );\n}\n\nfunction Value() {\n const serie = useTooltipSerie();\n return serie.formattedValue ? (\n <span className=\"ub-tooltip-serie-value\">{serie.formattedValue}</span>\n ) : (\n <span className=\"ub-tooltip-serie-value-empty\">–</span>\n );\n}\n\nfunction Color() {\n const serie = useTooltipSerie();\n return (\n <span\n style={{ backgroundColor: serie.color }}\n className=\"ub-tooltip-serie-color\"\n />\n );\n}\n\nfunction Label(props: PropsWithChildren) {\n return (\n <span className=\"ub-tooltip-serie-label ub-truncate\">{props.children}</span>\n );\n}\n\nfunction Attributes() {\n const { attributes } = useTooltipSerie();\n if (!attributes) return null;\n const attributeValues = Object.values(attributes);\n\n return (\n <div className=\"ub-tooltip-serie-attributes ub-truncate\">\n {attributeValues.map((attributeValue, index) => (\n <Fragment key={\"tooltip-\" + attributeValue}>\n <span className=\"ub-tooltip-serie-attribute-value\">\n {attributeValue}\n </span>\n {index < attributeValues.length - 1 && (\n <span\n data-text=\", \"\n className=\"ub-tooltip-serie-attribute-divider\"\n />\n )}\n </Fragment>\n ))}\n </div>\n );\n}\n\nexport function sortSeriesByValue({\n tooltipSerieList,\n invertSort,\n}: {\n tooltipSerieList: Array<TooltipSerie>;\n invertSort?: boolean;\n}): Array<TooltipSerie> {\n if (invertSort) {\n return tooltipSerieList.sort(\n (a, b) => (Number(a.value) || 0) - (Number(b.value) || 0),\n );\n } else {\n return tooltipSerieList.sort(\n (a, b) => (Number(b.value) || 0) - (Number(a.value) || 0),\n );\n }\n}\n\nexport function extractSuffix(label: string, skipUppercase?: boolean): string {\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\nexport function Tooltip({\n timestamp,\n tooltipSerieList: unsortedTooltipSerieList,\n timeZone,\n spansMultipleDays,\n hasMultipleMetrics,\n hasAttributes,\n invertSort,\n visibilityLimit,\n}: TooltipExtendedProps) {\n const tooltipSerieList = sortSeriesByValue({\n tooltipSerieList: unsortedTooltipSerieList,\n invertSort,\n });\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 const limit = visibilityLimit || DEFAULT_VISIBILITY_LIMIT;\n\n if (!hasAttributes) {\n return (\n <MetricsTooltip\n formattedTime={formattedTime}\n unitCategory={\"\"}\n tooltipSerieList={tooltipSerieList}\n visibilityLimit={limit}\n invertSort={invertSort}\n />\n );\n }\n\n if (hasAttributes && !hasMultipleMetrics) {\n return (\n <MultipleAttributesTooltip\n formattedTime={formattedTime}\n tooltipSerieList={tooltipSerieList}\n visibilityLimit={limit}\n invertSort={invertSort}\n />\n );\n }\n\n return (\n <MultipleAttributesMultipleMetricsTooltip\n formattedTime={formattedTime}\n unitCategory={\"\"}\n tooltipSerieList={tooltipSerieList}\n visibilityLimit={limit}\n invertSort={invertSort}\n />\n );\n}\n","import uPlot from \"uplot\";\nimport type { MetricMetadata, Serie, TooltipSerie } from \"../../types\";\nimport { tooltipManager } from \"./TooltipManager\";\nimport { Tooltip, TooltipProps } 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 checkIfSpansMultipleDays(\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 buildTooltipSerieList(\n u: uPlot,\n actualIdx: number,\n formatValue: (v: number) => string,\n stacked: boolean,\n metadata: Record<string, MetricMetadata>,\n series?: Serie[],\n timeZone?: string,\n): {\n tooltipSerieList: TooltipSerie[];\n hasAttributes: boolean;\n hasMultipleMetrics: boolean;\n hasMultipleAttributes: boolean;\n spansMultipleDays: boolean;\n} {\n const tooltipSerieList: TooltipSerie[] = [];\n const xData = u.data[0];\n\n // Track unique metrics and attributes efficiently\n const seenMetrics = new Set<string>();\n const seenAttributeKeys = new Set<string>();\n let hasAnyAttributes = false;\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\n if (!originalSerie) {\n console.warn(\"Original serie not found\");\n continue;\n }\n\n const metric = metadata[originalSerie.metric];\n if (!metric) {\n console.warn(\"Metric metadata not found\");\n continue;\n }\n\n seenMetrics.add(originalSerie.metric);\n\n const attributes = originalSerie?.attributes;\n if (attributes && Object.keys(attributes).length > 0) {\n hasAnyAttributes = true;\n\n for (const key in attributes) {\n seenAttributeKeys.add(key);\n }\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\n const formattedValue =\n rawVal == null\n ? undefined\n : formatValue\n ? formatValue(rawVal)\n : String(rawVal);\n\n tooltipSerieList.push({\n metric,\n color,\n value: rawVal === null ? undefined : rawVal,\n formattedValue,\n attributes,\n serie: s,\n });\n }\n\n return {\n tooltipSerieList,\n hasAttributes: hasAnyAttributes,\n hasMultipleMetrics: seenMetrics.size > 1,\n hasMultipleAttributes: seenAttributeKeys.size > 1,\n spansMultipleDays: checkIfSpansMultipleDays(xData, timeZone),\n };\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 timeZone?: string,\n appearance?: React.ComponentType<TooltipProps>,\n series?: Serie[],\n visibilityLimit?: number,\n invertSort?: 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 actualIdx = findNearestNonNullIndex(u, idx);\n const timestamp = u.data[0][actualIdx];\n\n if (timestamp === undefined) {\n return;\n }\n\n const {\n tooltipSerieList,\n hasAttributes,\n hasMultipleAttributes,\n hasMultipleMetrics,\n spansMultipleDays,\n } = buildTooltipSerieList(\n u,\n actualIdx,\n formatValue,\n stacked,\n metadata,\n series,\n timeZone,\n );\n\n tooltipManager.show();\n\n const AppearanceTooltip = appearance;\n const tooltipElement = AppearanceTooltip ? (\n <AppearanceTooltip\n timestamp={timestamp}\n tooltipSerieList={tooltipSerieList}\n timeZone={timeZone}\n />\n ) : (\n <Tooltip\n timestamp={timestamp}\n tooltipSerieList={tooltipSerieList}\n timeZone={timeZone}\n spansMultipleDays={spansMultipleDays}\n stacked={stacked}\n hasAttributes={hasAttributes}\n hasMultipleAttributes={hasMultipleAttributes}\n hasMultipleMetrics={hasMultipleMetrics}\n visibilityLimit={visibilityLimit}\n invertSort={invertSort}\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\";\nimport { StackedData } from \"@/types\";\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 // Percentunit ranges from 0 to 1\n const percentMax = unit === \"percent\" ? 100 : 1;\n if (dataMax > percentMax) {\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, percentMax),\n ];\n }\n\n // Normal case\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, percentMax),\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 hideAxis?: boolean,\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: (self, _axisIdx, _scaleMin, _scaleMax, _plotDim) => {\n const height = self.height;\n if (height <= 100) {\n return 30; // At least 2 ticks, better if 3\n } if (height <= 150) {\n return 35; // Bettween 3 and 4 ticks\n } if (height <= 200) {\n return 45; // Between 3 and 4 ticks\n } else if (height <= 250) {\n return 55; // Between 3 and 4 ticks\n } if (height <= 300) {\n return 60; // Between 4 and 5 ticks\n } else {\n return 70; // At least 4 ticks\n }\n },\n show: !hideAxis,\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(data: uPlot.AlignedData, omit: boolean): StackedData {\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: Array<uPlot.AlignedData[number]> = [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 if (!omit) {\n for (let i = 1; i < data.length - 1; i++) {\n bands.push({\n series: [i + 1, i],\n });\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","hideAxis","hideCursor","relativeTimeAxis","invertSort","parentContext","useContext","LoadingComponent","ErrorComponent","TooltipComponent","EmptyComponent","tooltipHide","tooltipVisibilityLimit","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","fill","hideAxis","hideCursor","relativeTimeAxis","invertSort","UnblindClientProvider","Scope","useQuery","useMetrics","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","useQuery","res","metrics","useQuery","useMemo","ms","getTimeConfig","scope","props","timeRangeToCalculatedTimestamp","timeRange","now","ms","deduceTimestamp","startTime","endTime","calculatedStartTime","calculatedEndTime","useMemo","useTimeConfig","props","scope","useScope","timeRange","startTime","endTime","getTimeConfig","useTimeseries","params","apiBaseUrl","fetchImpl","useUnblindClientConfig","startTime","endTime","timeRange","useTimeConfig","queries","interval","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","params","apiBaseUrl","fetchImpl","useUnblindClientConfig","startTime","endTime","timeRange","useTimeConfig","query","useQuery","calculatedStartTime","calculatedEndTime","deduceTimestamp","endpoint","res","usage","useMemo","isLoading","hasError","useInfiniteQuery","useMemo","useLogs","props","apiBaseUrl","fetchImpl","useUnblindClientConfig","timeRange","startTime","endTime","useTimeConfig","attributes","body","severity","traceId","spanId","logId","query","useInfiniteQuery","pageParam","deducedStartTime","deducedEndTime","deduceTimestamp","res","data","nextPage","lastPage","useMemo","page","useEffect","useMemo","useEffect","useRef","uPlot","isLineThreshold","threshold","isRangeThreshold","uPlot","getValueFormat","dateTimeFormat","dateTimeFormatTimeAgo","systemDateFormats","timeUnitSize","timeIncrs","formatTime","splits","foundIncr","range","timeZone","format","systemDateFormats","yearRoundedToDay","v","date","dateTimeFormat","showSeconds","showMillis","checkTimeStr","findBestIncrement","targetSeconds","scales","scale","mult","incr","lastScale","lastMult","generateRelativeXAxisSplits","u","min","max","generateRelativeXAxisValues","timestamp","index","dateTimeFormatTimeAgo","generateXAxisValues","approxTicks","increment","splitsInMs","s","createXAxisConfig","fontFamily","relativeTimeAxis","hideAxis","_","values","getChartFont","_self","_axisIdx","_scaleMin","_scaleMax","plotDim","computePosition","flip","offset","createRoot","TOOLTIP_DISTANCE_CURSOR","TOOLTIP_PADDING_FROM_CURSOR","TooltipManager","u","content","anchor","overlay","x","y","tooltipManager","dateTimeFormat","createContext","Fragment","useContext","jsx","jsxs","DEFAULT_VISIBILITY_LIMIT","SerieContext","useTooltipSerie","context","SerieProvider","serie","children","Divider","className","props","MetricsTooltip","formattedTime","unitCategory","tooltipSerieList","visibilityLimit","invertSort","visibleSeries","afterLimit","Header","DateTime","UnitCategory","Content","x","Serie","Color","Label","Metric","Value","Summary","MultipleAttributesTooltip","firstSerie","extractSuffix","i","Attributes","MultipleAttributesMultipleMetricsTooltip","series","formattedVal","allZeroes","allUndefined","attributes","attributeValues","attributeValue","index","sortSeriesByValue","a","b","label","skipUppercase","parts","last","Tooltip","timestamp","unsortedTooltipSerieList","timeZone","spansMultipleDays","hasMultipleMetrics","hasAttributes","limit","jsx","checkIfSpansMultipleDays","xData","timeZone","minTimestamp","maxTimestamp","minDate","maxDate","getDateString","date","findNearestNonNullIndex","u","idx","hasNonNullValue","seriesIndex","dataLength","distance","leftCandidate","rightCandidate","buildTooltipSerieList","actualIdx","formatValue","stacked","metadata","series","tooltipSerieList","seenMetrics","seenAttributeKeys","hasAnyAttributes","rawVal","prevVal","s","originalSerie","metric","attributes","key","stroke","color","formattedValue","tooltipPlugin","appearance","visibilityLimit","invertSort","over","boundingLeft","boundingTop","isHovering","syncBounds","bbox","tooltipManager","left","top","timestamp","hasAttributes","hasMultipleAttributes","hasMultipleMetrics","spansMultipleDays","AppearanceTooltip","tooltipElement","Tooltip","DEFAULT_COLORS","getChartFont","fontFamiliy","compareAndResolveUnit","paramsUnit","metricMetadata","unit","isColorsObject","colors","isColorsArray","MAX_COLORS","getFillingColor","isFilling","useSolid","index","colorIndex","getColor","serie","colors","type","computedStyle","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","createChartOptions","container","metadata","timeZone","tooltipComponent","relativeTimeAxis","hideTooltip","predefinedMin","predefinedMax","hideAxis","hideCursor","visibilityLimit","invertSort","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","percentMax","niceMax","niceMin","calculateYAxisSize","self","values","axisIdx","cycleNum","axis","axisSize","longestVal","acc","val","createYAxisConfig","formatter","fontFamiliy","hideAxis","getChartFont","vals","v","formmatedVal","_axisIdx","_scaleMin","_scaleMax","_plotDim","height","stack","data","omit","bands","xAxis","xAxisLen","accum","accumData","serie","i","value","index","b","jsx","Chart","props","times","series","metadata","type","className","timeZone","propsOptions","tooltip","propsColors","tooltipComponent","propsUnit","fill","thresholds","predefinedMin","predefinedMax","hideAxis","hideCursor","relativeTimeAxis","invertSort","hideTooltip","visibilityLimit","chartRef","useRef","scope","useScope","colors","useEffect","data","unit","serie","metricMetadata","compareAndResolveUnit","stacked","stackedData","stack","threshold","isLineThreshold","isRangeThreshold","container","u","opts","createChartOptions","uPlot","resizeObserver","jsx","Timeseries","metrics","propOperator","propAttributes","propGroupBy","propInterval","type","className","propAppearance","propUnit","propThresholds","propMin","propMax","propColors","propTooltip","propFill","propHideAxis","propHideCursor","propRelativeTimeAxis","onResponse","propInvertSort","scope","useScope","interval","attributes","groupBy","operator","relativeTimeAxis","colors","fill","invertSort","hideAxis","hideCursor","tooltipComponent","tooltip","useMemo","response","useTimeseries","metric","isLoading","data","hasError","series","times","metadata","isEmpty","x","containerClassName","useEffect","LoadingComponent","ErrorComponent","EmptyComponent","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/useTimeConfig.ts","../src/hooks/useUsage.ts","../src/hooks/useLogs.ts","../src/types.ts","../src/components/Timeseries/index.tsx","../src/components/Chart/index.tsx","../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 hideAxis,\n hideCursor,\n relativeTimeAxis,\n invertSort,\n disableSuggestedLabel,\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 tooltipHide =\n typeof tooltip?.hide === \"boolean\"\n ? tooltip?.hide\n : parentContext?.tooltip?.hide;\n const tooltipVisibilityLimit =\n typeof tooltip?.visibilityLimit === \"number\"\n ? tooltip?.visibilityLimit\n : parentContext?.tooltip?.visibilityLimit;\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 hide: tooltipHide,\n visibilityLimit: tooltipVisibilityLimit,\n };\n }, [tooltipHide, tooltipVisibilityLimit]);\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 relativeTimeAxis:\n typeof relativeTimeAxis === \"boolean\"\n ? relativeTimeAxis\n : parentContext?.relativeTimeAxis,\n fill: typeof fill === \"boolean\" ? fill : parentContext?.fill,\n hideAxis:\n typeof hideAxis === \"boolean\" ? hideAxis : parentContext?.hideAxis,\n hideCursor:\n typeof hideCursor === \"boolean\"\n ? hideCursor\n : parentContext?.hideCursor,\n invertSort:\n typeof invertSort === \"boolean\"\n ? invertSort\n : parentContext?.invertSort,\n disableSuggestedLabel:\n typeof disableSuggestedLabel === \"boolean\"\n ? disableSuggestedLabel\n : parentContext?.disableSuggestedLabel,\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 fill,\n relativeTimeAxis,\n hideAxis,\n hideCursor,\n memoizedAppearance,\n memoizedTooltip,\n parentContext,\n invertSort,\n disableSuggestedLabel,\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","export 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-default-error\" />;\n}\n\nexport function Loading() {\n return <div className=\"ub-default-loading\" 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 fill,\n hideAxis,\n hideCursor,\n relativeTimeAxis,\n invertSort,\n disableSuggestedLabel,\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 fill={fill}\n hideAxis={hideAxis}\n hideCursor={hideCursor}\n relativeTimeAxis={relativeTimeAxis}\n invertSort={invertSort}\n disableSuggestedLabel={disableSuggestedLabel}\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 { useTimeConfig } from \"@/hooks/useTimeConfig\";\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 UseTimeseriesData {\n series: Serie[];\n times: number[];\n metadata: Record<string, MetricMetadata>;\n}\n\nexport interface UseTimeseriesReturn {\n data: UseTimeseriesData;\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 params: UseTimeseriesParams,\n): UseTimeseriesReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const { startTime, endTime, timeRange } = useTimeConfig(params);\n const { queries, interval } = params;\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 hasValidTimeConfig({\n timeRange,\n startTime,\n endTime,\n}: {\n timeRange?: TimeRange;\n startTime?: number;\n endTime?: number;\n}) {\n return (\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange\n );\n}\n\nexport function getTimeConfig({\n scope,\n props,\n}: {\n scope: {\n timeRange: TimeRange;\n startTime?: number;\n endTime?: number;\n };\n props: {\n timeRange?: TimeRange;\n startTime?: number;\n endTime?: number;\n };\n}) {\n if (props.timeRange || (props.startTime && props.endTime)) {\n return props;\n } else {\n return scope;\n }\n}\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 { getTimeConfig } from \"./utils\";\nimport { useScope } from \"@/providers\";\nimport { useMemo } from \"react\";\nimport ms from \"ms\";\n\nexport function useTimeConfig(props: {\n startTime?: number;\n endTime?: number;\n timeRange?: ms.StringValue;\n}) {\n const scope = useScope();\n\n const { timeRange, startTime, endTime } = getTimeConfig({\n props,\n scope,\n });\n\n return useMemo(\n () => ({\n timeRange,\n startTime,\n endTime,\n }),\n [timeRange, startTime, endTime],\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport { TimeConfig } from \"@/types\";\nimport { useTimeConfig } from \"@/hooks/useTimeConfig\";\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(params: UseUsageParams): UseUsageReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const { startTime, endTime, timeRange } = useTimeConfig(params);\n\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 { useTimeConfig } from \"@/hooks/useTimeConfig\";\nimport type { Log, PaginatedResponse, TimeConfig } from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport type UseLogsParams = {\n /**\n * Array of filters to apply to the logs query.\n */\n attributes?: Record<string, Array<string>>;\n body?: Array<string>;\n severity?: Array<string>;\n traceId?: string;\n spanId?: string;\n logId?: string;\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(props: UseLogsParams): UseLogsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const { timeRange, startTime, endTime } = useTimeConfig(props);\n const { attributes, body, severity, traceId, spanId, logId } = props;\n\n const query = useInfiniteQuery<PaginatedLogsResponse>({\n queryKey: [\n \"unblind\",\n \"logs\",\n timeRange,\n startTime,\n endTime,\n body,\n severity,\n logId,\n JSON.stringify(attributes),\n ],\n queryFn: async ({ pageParam }) => {\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 attributes,\n body,\n severity,\n traceId,\n spanId,\n logId,\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, nextPage } = await res.json();\n\n if (!data) {\n throw new Error(\"data not found\");\n }\n\n return { data, nextPage } as PaginatedLogsResponse;\n },\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => lastPage.nextPage,\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","/**\n * Shared types for Unblind React library.\n */\n\nimport { StringValue } from \"ms\";\nimport { TooltipProps } 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 * Metric metadata returned by the API.\n */\nexport interface MetricMetadata {\n name: string;\n description: string;\n suggestedLabel?: 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}\n\nexport type LabeledMetricMetadata = MetricMetadata & { label?: string };\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 queryIndex: number;\n isEmpty?: boolean;\n}\n\n/**\n * Query definition for timeseries requests.\n */\nexport interface TimeseriesQuery {\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 traceId?: string;\n spanId?: string;\n logId?: string;\n severity: Severity;\n attributes?: Record<string, string>;\n body?: string;\n serviceName?: string;\n}\n\n/**\n * Default paginated response structure\n */\nexport type PaginatedResponse<T> = {\n data: Array<T>;\n nextPage?: 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 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 /**\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 * Optional boolean to hide both axis\n */\n hideAxis?: boolean;\n\n /**\n * Optional boolean to hide cursor\n */\n hideCursor?: boolean;\n\n /**\n * Optional boolean to display relative time axis\n *\n * e.g. a day ago ------ a few seconds ago\n */\n relativeTimeAxis?: boolean;\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 invert value order.\n * Defaults to false\n */\n invertSort?: boolean;\n\n /**\n * Disables suggested label\n */\n disableSuggestedLabel?: boolean;\n}\n\nexport interface TooltipConfig {\n hide?: boolean;\n visibilityLimit?: number;\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\nexport type StackedData = {\n data: Array<uPlot.AlignedData[number]>;\n bands: Array<uPlot.Band>;\n};\n\nexport interface TooltipSerie {\n metric: LabeledMetricMetadata;\n serie: uPlot.Series;\n color: string;\n value?: number;\n formattedValue?: string;\n attributes?: Record<string, string>;\n}\n\nexport interface LabeledMetric {\n name: string;\n label: string;\n}\n\nexport function isLabeledMetric(value: unknown): value is LabeledMetric {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as LabeledMetric).name === \"string\" &&\n typeof (value as LabeledMetric).label === \"string\"\n );\n}\n","import { useTimeseries, UseTimeseriesReturn } from \"../../hooks/useTimeseries\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport {\n type Appearance,\n type TimeseriesQueryConfig,\n type ChartVisualConfig,\n isLabeledMetric,\n LabeledMetric,\n LabeledMetricMetadata,\n} from \"../../types\";\nimport { useEffect, 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 * @example\n * Labeled metrics\n * ```jsx\n * <Timeseries metrics={{ name: \"nodejs.eventloop.p50\", label: \"P50\" ]} />\n * ```\n */\n metrics: Array<LabeledMetric> | Array<string> | 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 * Hook to get request data\n */\n onResponse?: (response: UseTimeseriesReturn) => void;\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 tooltip: propTooltip,\n onResponse,\n ...chartStyleProps\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\n const tooltipComponent =\n propAppearance?.components?.Tooltip ?? scope.appearance.components.Tooltip;\n\n const tooltip = useMemo(() => {\n return {\n hide: propTooltip?.hide ?? scope.tooltip?.hide,\n visibilityLimit:\n propTooltip?.visibilityLimit ?? scope.tooltip?.visibilityLimit,\n };\n }, [\n propTooltip?.hide,\n scope.tooltip?.hide,\n propTooltip?.visibilityLimit,\n scope.tooltip?.visibilityLimit,\n ]);\n\n const { queries, metadataLabels } = useMemo(() => {\n const normalizedMetrics = Array.isArray(metrics) ? metrics : [metrics];\n const metadataLabels: Record<string, string> = {};\n\n const queries = normalizedMetrics.map((metric) => {\n const name = isLabeledMetric(metric) ? metric.name : metric;\n if (isLabeledMetric(metric)) metadataLabels[name] = metric.label;\n return { metrics: [name], operator, attributes, groupBy };\n });\n\n return { queries, metadataLabels };\n }, [metrics, operator, attributes, groupBy]);\n\n const response = useTimeseries({\n queries,\n timeRange: scope.timeRange,\n startTime: scope.startTime,\n endTime: scope.endTime,\n interval,\n });\n const { isLoading, data, hasError } = response;\n\n const { series, times, metadata } = data;\n const isEmpty = series.every((x) => x.isEmpty);\n\n const labeledMetadata = useMemo((): Record<string, LabeledMetricMetadata> => {\n if (Object.keys(metadataLabels).length === 0) return metadata;\n\n return Object.fromEntries(\n Object.entries(metadata).map(([metricName, meta]) => [\n metricName,\n {\n ...meta,\n label:\n metadataLabels[metricName] ?? meta.suggestedLabel ?? metricName,\n },\n ]),\n );\n }, [metadataLabels, metadata]);\n\n const containerClassName = `ub-chart-container${className ? ` ${className}` : \"\"}`;\n\n useEffect(() => {\n if (onResponse) {\n onResponse(response);\n }\n }, [response, onResponse]);\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={labeledMetadata}\n type={type}\n className={className}\n tooltip={tooltip}\n tooltipComponent={tooltipComponent}\n {...chartStyleProps}\n />\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport uPlot from \"uplot\";\nimport {\n type Serie,\n type ChartVisualConfig,\n type ChartType,\n isLineThreshold,\n isRangeThreshold,\n type LabeledMetricMetadata,\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, LabeledMetricMetadata>;\n type: ChartType;\n invertSort?: boolean;\n className?: string;\n timeZone?: string;\n options?: uPlot.Options;\n tooltipComponent?: React.ComponentType<TooltipProps>;\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: propColors,\n tooltipComponent,\n unit: propsUnit,\n fill: propFill,\n thresholds,\n min: predefinedMin,\n max: predefinedMax,\n hideAxis: propHideAxis,\n hideCursor: propHideCursor,\n relativeTimeAxis: propRelativeTimeAxis,\n invertSort: propInvertSort,\n disableSuggestedLabel: propDisableSuggestedLabel,\n } = props;\n const { hide: propHideTooltip, visibilityLimit: propVisibilityLimit } =\n tooltip || {};\n const chartRef = useRef<HTMLDivElement>(null);\n const scope = useScope();\n const relativeTimeAxis =\n typeof propRelativeTimeAxis === \"boolean\"\n ? propRelativeTimeAxis\n : scope.relativeTimeAxis;\n const colors = propColors ?? scope.colors;\n const fill = typeof propFill === \"boolean\" ? propFill : scope.fill || false;\n const invertSort =\n typeof propInvertSort === \"boolean\" ? propInvertSort : scope.invertSort;\n const disableSuggestedLabel =\n typeof propDisableSuggestedLabel === \"boolean\"\n ? propDisableSuggestedLabel\n : scope.disableSuggestedLabel;\n const hideAxis =\n typeof propHideAxis === \"boolean\" ? propHideAxis : scope.hideAxis;\n const hideCursor =\n typeof propHideCursor === \"boolean\" ? propHideCursor : scope.hideCursor;\n const visibilityLimit =\n typeof propVisibilityLimit === \"boolean\"\n ? propVisibilityLimit\n : scope.tooltip?.visibilityLimit;\n const hideTooltip =\n typeof propHideTooltip === \"boolean\"\n ? propHideTooltip\n : scope.tooltip?.hide;\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 const stacked = type === \"bar\" || type === \"area\";\n const stackedData = stack(data, !stacked);\n\n if (thresholds) {\n thresholds.forEach((threshold) => {\n if (isLineThreshold(threshold)) {\n stackedData.data.push(new Array(times.length).fill(threshold.value));\n } else if (isRangeThreshold(threshold)) {\n stackedData.data.push(new Array(times.length).fill(threshold.from));\n stackedData.data.push(new Array(times.length).fill(threshold.to));\n }\n });\n }\n\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 timeZone,\n tooltipComponent,\n relativeTimeAxis,\n hideTooltip,\n thresholds,\n predefinedMin,\n predefinedMax,\n hideAxis,\n hideCursor,\n visibilityLimit,\n invertSort,\n disableSuggestedLabel,\n );\n\n u = new uPlot(\n { ...opts, ...propsOptions },\n stackedData.data as uPlot.AlignedData,\n container,\n );\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 relativeTimeAxis,\n colors,\n fill,\n propsUnit,\n hideTooltip,\n thresholds,\n predefinedMin,\n predefinedMax,\n propsOptions,\n hideAxis,\n hideCursor,\n invertSort,\n visibilityLimit,\n disableSuggestedLabel,\n ]);\n\n return (\n <div\n ref={chartRef}\n className={\"ub-chart-container\" + (className ? ` ${className}` : \"\")}\n />\n );\n}\n","import {\n ChartType,\n Colors,\n isLineThreshold,\n isRangeThreshold,\n MetricMetadata,\n Serie,\n StackedData,\n Threshold,\n ThresholdLevel,\n ThresholdLineType,\n} from \"@/types\";\n\nimport uPlot from \"uplot\";\nimport { getValueFormat } from \"@unblind/units\";\nimport { createXAxisConfig } from \"./XAxis\";\nimport { buildScaleRange, createYAxisConfig } from \"./YAxis\";\nimport { 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\nconst MAX_COLORS = 12;\n\nconst getFillingColor = ({\n isFilling,\n useSolid,\n index,\n}: {\n isFilling?: boolean;\n useSolid: boolean;\n index: number;\n}) => {\n const colorIndex = (index % MAX_COLORS) + 1;\n\n if (isFilling) {\n return useSolid\n ? `--ub-chart-serie-color-${colorIndex}`\n : `--ub-chart-serie-fill-color-${colorIndex}`;\n }\n\n return `--ub-chart-serie-color-${colorIndex}`;\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 const useSolid = type === \"bar\" || type === \"area\";\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 = getFillingColor({ isFilling, useSolid, index });\n const cssColor = computedStyle.getPropertyValue(cssColorVar).trim();\n\n if (cssColor) {\n return cssColor;\n }\n\n // Static fallback\n return isFilling && useSolid\n ? DEFAULT_COLORS[index % DEFAULT_COLORS.length]?.replace(\")\", \" / 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: StackedData,\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 (isLineThreshold(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 }\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: StackedData,\n unit: string | undefined,\n series: Array<Serie>,\n type: ChartType,\n stacked: boolean,\n colors: Colors | undefined,\n fill: boolean,\n timeZone?: string,\n tooltipComponent?: React.ComponentType<TooltipProps>,\n relativeTimeAxis?: boolean,\n hideTooltip?: boolean,\n thresholds?: Array<Threshold>,\n predefinedMin?: number,\n predefinedMax?: number,\n hideAxis?: boolean,\n hideCursor?: boolean,\n visibilityLimit?: number,\n invertSort?: boolean,\n disableSuggestedLabel?: boolean,\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);\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: hideTooltip\n ? []\n : [\n tooltipPlugin(\n formattingFunction,\n stacked,\n metadata,\n timeZone,\n tooltipComponent,\n series,\n visibilityLimit,\n invertSort,\n disableSuggestedLabel,\n ),\n ],\n // [top, right, bottom, left];\n padding: relativeTimeAxis ? [8, 10, 8, 48] : [8, 10, 8, 18],\n cursor: {\n y: false,\n sync: { key: \"_\" },\n drag: {\n setScale: true,\n x: true,\n y: false,\n },\n move: cursorMovement,\n show: !hideCursor,\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, hideAxis),\n createYAxisConfig(formatter, fontFamily, hideAxis),\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 return splits.map((v) => {\n const date = new Date(v);\n const day = timeZone === \"UTC\" ? date.getUTCDate() : date.getDate();\n\n if (day === 1) {\n return date.toLocaleDateString(undefined, {\n month: \"short\",\n year: \"numeric\",\n timeZone,\n });\n } else {\n return date.toLocaleDateString(undefined, {\n day: \"numeric\",\n month: \"short\",\n timeZone,\n });\n }\n });\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 */\n// function 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 hideAxis: boolean = false,\n): uPlot.Axis {\n const splits: uPlot.Axis.Splits | undefined = relativeTimeAxis\n ? (u, _, min, max) => generateRelativeXAxisSplits(u, min, max)\n : undefined;\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 show: !hideAxis,\n align: relativeTimeAxis ? 2 : undefined,\n space: (_self, _axisIdx, _scaleMin, _scaleMax, plotDim) => {\n if (plotDim < 400) {\n return 100;\n } else if (plotDim < 800) {\n return 150;\n } else {\n return 250;\n }\n },\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 { createContext, Fragment, PropsWithChildren, useContext } from \"react\";\nimport { TooltipSerie } from \"@/types\";\n\n// Types\nexport interface TooltipProps {\n timestamp: number;\n tooltipSerieList: TooltipSerie[];\n timeZone?: string;\n stacked?: boolean;\n invertSort?: boolean;\n visibilityLimit?: number;\n disableSuggestedLabel?: boolean;\n}\n\ninterface TooltipExtendedProps extends TooltipProps {\n spansMultipleDays?: boolean;\n hasMultipleMetrics: boolean;\n hasMultipleAttributes: boolean;\n hasAttributes: boolean;\n}\n\nconst DEFAULT_VISIBILITY_LIMIT = 6;\n\n// Context for serie\nconst SerieContext = createContext<TooltipSerie | null>(null);\n\nfunction useTooltipSerie() {\n const context = useContext(SerieContext);\n if (!context) {\n throw new Error(\"useTooltipSerie must be used within a SerieProvider\");\n }\n return context;\n}\n\nfunction SerieProvider({\n serie,\n children,\n}: PropsWithChildren<{ serie: TooltipSerie }>) {\n return (\n <SerieContext.Provider value={serie}>{children}</SerieContext.Provider>\n );\n}\n// Context for tooltip\nconst TooltipContext = createContext<\n | (Pick<\n TooltipProps,\n \"disableSuggestedLabel\" | \"invertSort\" | \"visibilityLimit\"\n > & { formattedTime: string })\n | null\n>(null);\n\nfunction useTooltip() {\n const context = useContext(TooltipContext);\n if (!context) {\n throw new Error(\"useTooltipSerie must be used within a SerieProvider\");\n }\n return context;\n}\n\nfunction TooltipProvider({\n formattedTime,\n tooltip,\n children,\n}: PropsWithChildren & {\n formattedTime: string;\n tooltip: Pick<\n TooltipProps,\n \"disableSuggestedLabel\" | \"invertSort\" | \"visibilityLimit\"\n >;\n}) {\n return (\n <TooltipContext.Provider\n value={{\n formattedTime,\n disableSuggestedLabel: tooltip.disableSuggestedLabel,\n invertSort: tooltip.invertSort,\n visibilityLimit: tooltip.visibilityLimit || DEFAULT_VISIBILITY_LIMIT,\n }}\n >\n {children}\n </TooltipContext.Provider>\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\nfunction MetricsTooltip({\n unitCategory,\n tooltipSerieList,\n}: {\n unitCategory: string;\n tooltipSerieList: Array<TooltipSerie>;\n}) {\n const { visibilityLimit, formattedTime } = useTooltip();\n const visibleSeries = tooltipSerieList.slice(0, visibilityLimit);\n const afterLimit = tooltipSerieList.slice(visibilityLimit);\n\n return (\n <div className=\"ub-tooltip ub-tooltip-multiple-metrics\">\n <Header>\n <DateTime>{formattedTime}</DateTime>\n <div className=\"ub-tooltip-header-right\">\n <UnitCategory>{unitCategory}</UnitCategory>\n </div>\n </Header>\n <Divider />\n <div>\n <Content>\n {visibleSeries.map((x) => (\n <Serie serie={x} key={x.metric.name}>\n <Color />\n <Metric />\n <Value />\n </Serie>\n ))}\n </Content>\n <Summary series={afterLimit} />\n </div>\n </div>\n );\n}\n\nfunction MultipleAttributesTooltip({\n tooltipSerieList,\n}: {\n tooltipSerieList: Array<TooltipSerie>;\n}) {\n const { visibilityLimit, disableSuggestedLabel, formattedTime } =\n useTooltip();\n const firstSerie = tooltipSerieList[0];\n\n const visibleSeries = tooltipSerieList.slice(0, visibilityLimit);\n const afterLimit = tooltipSerieList.slice(visibilityLimit);\n\n return (\n <div className=\"ub-tooltip ub-tooltip-multiple-attributes\">\n <Header>\n <DateTime>{formattedTime}</DateTime>\n <div className=\"ub-tooltip-header-right\">\n <span className=\"ub-tooltip-serie-metric\">\n {firstSerie?.metric.label ||\n (!disableSuggestedLabel && firstSerie?.metric.suggestedLabel) ||\n firstSerie?.metric.name}\n </span>\n </div>\n </Header>\n <Divider />\n <div>\n <Content>\n {visibleSeries.map((x, i) => (\n <Serie serie={x} key={\"serie_\" + i}>\n <Color />\n <Attributes />\n <Value />\n </Serie>\n ))}\n </Content>\n <Summary series={afterLimit} />\n </div>\n </div>\n );\n}\n\nfunction MultipleAttributesMultipleMetricsTooltip({\n tooltipSerieList,\n unitCategory,\n}: {\n unitCategory: string;\n tooltipSerieList: Array<TooltipSerie>;\n}) {\n const { visibilityLimit, formattedTime } = useTooltip();\n const visibleSeries = tooltipSerieList.slice(0, visibilityLimit);\n const afterLimit = tooltipSerieList.slice(visibilityLimit);\n\n return (\n <div className=\"ub-tooltip ub-tooltip-multiple-metrics-attributes\">\n <Header>\n <DateTime>{formattedTime}</DateTime>\n <div className=\"ub-tooltip-header-right\">\n <span className=\"ub-tooltip-unit-category\">{unitCategory}</span>\n </div>\n </Header>\n <Divider />\n <div>\n <Content>\n {visibleSeries.map((x, i) => (\n <Serie serie={x} key={\"serie\" + i}>\n <Color />\n <Metric />\n <Attributes />\n <Value />\n </Serie>\n ))}\n </Content>\n <Summary series={afterLimit} />\n </div>\n </div>\n );\n}\n\nfunction UnitCategory(props: PropsWithChildren) {\n return <span className=\"ub-tooltip-unit-category\">{props.children}</span>;\n}\n\nfunction Summary({ series }: { series: Array<TooltipSerie> }) {\n const formattedVal = series[0]?.formattedValue;\n const allZeroes = !series.some((x) => (x.value || 0) > 0);\n const allUndefined = !series.some((x) => x.value !== undefined);\n const { invertSort } = useTooltip();\n\n if (series.length > 0) {\n if (allUndefined) {\n return (\n <span className=\"ub-tooltip-summary\">\n <span>+{series.length} more with no data</span>\n </span>\n );\n }\n\n if (allZeroes) {\n return (\n <span className=\"ub-tooltip-summary\">\n <span>+{series.length} more with zero values</span>\n </span>\n );\n }\n\n return (\n <span className=\"ub-tooltip-summary\">\n <span>+{series.length} more with </span>\n <span>{`${invertSort ? \"≥\" : \"≤\"} ${formattedVal}`}</span>\n </span>\n );\n }\n return <></>;\n}\n\nfunction Serie(props: PropsWithChildren & { serie: TooltipSerie }) {\n return (\n <SerieProvider serie={props.serie}>\n <div className=\"ub-tooltip-serie\">{props.children}</div>\n </SerieProvider>\n );\n}\n\nfunction Content(props: PropsWithChildren) {\n return <div className=\"ub-tooltip-content\">{props.children}</div>;\n}\n\nfunction Header(props: PropsWithChildren) {\n return <div className=\"ub-tooltip-header\">{props.children}</div>;\n}\n\nfunction DateTime(props: PropsWithChildren) {\n return <div className=\"ub-tooltip-datetime\">{props.children}</div>;\n}\n\nfunction Metric() {\n const serie = useTooltipSerie();\n const { disableSuggestedLabel } = useTooltip();\n return (\n <span className=\"ub-tooltip-serie-metric ub-truncate\">\n {serie.metric.label ||\n (!disableSuggestedLabel && serie.metric.suggestedLabel) ||\n serie.metric.name}\n </span>\n );\n}\n\nfunction Value() {\n const serie = useTooltipSerie();\n return serie.formattedValue ? (\n <span className=\"ub-tooltip-serie-value ub-truncate\">\n {serie.formattedValue}\n </span>\n ) : (\n <span className=\"ub-tooltip-serie-value-empty\">–</span>\n );\n}\n\nfunction Color() {\n const serie = useTooltipSerie();\n return (\n <span\n style={{ backgroundColor: serie.color }}\n className=\"ub-tooltip-serie-color\"\n />\n );\n}\n\nfunction Attributes() {\n const { attributes } = useTooltipSerie();\n if (!attributes) return null;\n const attributeValues = Object.values(attributes);\n\n return (\n <div className=\"ub-tooltip-serie-attributes ub-truncate\">\n {attributeValues.map((attributeValue, index) => (\n <Fragment key={\"tooltip-\" + attributeValue}>\n <span className=\"ub-tooltip-serie-attribute-value\">\n {attributeValue}\n </span>\n {index < attributeValues.length - 1 && (\n <span\n data-text=\", \"\n className=\"ub-tooltip-serie-attribute-divider\"\n />\n )}\n </Fragment>\n ))}\n </div>\n );\n}\n\nexport function sortSeriesByValue({\n tooltipSerieList,\n invertSort,\n}: {\n tooltipSerieList: Array<TooltipSerie>;\n invertSort?: boolean;\n}): Array<TooltipSerie> {\n if (invertSort) {\n return tooltipSerieList.sort(\n (a, b) => (Number(a.value) || 0) - (Number(b.value) || 0),\n );\n } else {\n return tooltipSerieList.sort(\n (a, b) => (Number(b.value) || 0) - (Number(a.value) || 0),\n );\n }\n}\n\nexport function Tooltip({\n timestamp,\n tooltipSerieList: unsortedTooltipSerieList,\n timeZone,\n spansMultipleDays,\n hasMultipleMetrics,\n hasAttributes,\n invertSort,\n visibilityLimit,\n disableSuggestedLabel,\n}: TooltipExtendedProps) {\n const tooltipSerieList = sortSeriesByValue({\n tooltipSerieList: unsortedTooltipSerieList,\n invertSort,\n });\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 if (!hasAttributes) {\n return (\n <TooltipProvider\n tooltip={{\n invertSort,\n visibilityLimit,\n disableSuggestedLabel,\n }}\n formattedTime={formattedTime}\n >\n <MetricsTooltip unitCategory={\"\"} tooltipSerieList={tooltipSerieList} />\n </TooltipProvider>\n );\n }\n\n if (hasAttributes && !hasMultipleMetrics) {\n return (\n <TooltipProvider\n tooltip={{\n invertSort,\n visibilityLimit,\n disableSuggestedLabel,\n }}\n formattedTime={formattedTime}\n >\n <MultipleAttributesTooltip tooltipSerieList={tooltipSerieList} />\n </TooltipProvider>\n );\n }\n\n return (\n <TooltipProvider\n tooltip={{\n invertSort,\n visibilityLimit,\n disableSuggestedLabel,\n }}\n formattedTime={formattedTime}\n >\n <MultipleAttributesMultipleMetricsTooltip\n unitCategory={\"\"}\n tooltipSerieList={tooltipSerieList}\n />\n </TooltipProvider>\n );\n}\n","import uPlot from \"uplot\";\nimport type { LabeledMetricMetadata, Serie, TooltipSerie } from \"../../types\";\nimport { tooltipManager } from \"./TooltipManager\";\nimport { Tooltip, TooltipProps } 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 checkIfSpansMultipleDays(\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 buildTooltipSerieList(\n u: uPlot,\n actualIdx: number,\n formatValue: (v: number) => string,\n stacked: boolean,\n metadata: Record<string, LabeledMetricMetadata>,\n series?: Serie[],\n timeZone?: string,\n): {\n tooltipSerieList: TooltipSerie[];\n hasAttributes: boolean;\n hasMultipleMetrics: boolean;\n hasMultipleAttributes: boolean;\n spansMultipleDays: boolean;\n} {\n const tooltipSerieList: TooltipSerie[] = [];\n const xData = u.data[0];\n\n // Track unique metrics and attributes efficiently\n const seenMetrics = new Set<string>();\n const seenAttributeKeys = new Set<string>();\n let hasAnyAttributes = false;\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\n if (!originalSerie) {\n console.warn(\"Original serie not found\");\n continue;\n }\n\n const metric = metadata[originalSerie.metric];\n if (!metric) {\n console.warn(\"Metric metadata not found\");\n continue;\n }\n\n seenMetrics.add(originalSerie.metric);\n\n const attributes = originalSerie?.attributes;\n if (attributes && Object.keys(attributes).length > 0) {\n hasAnyAttributes = true;\n\n for (const key in attributes) {\n seenAttributeKeys.add(key);\n }\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\n const formattedValue =\n rawVal == null\n ? undefined\n : formatValue\n ? formatValue(rawVal)\n : String(rawVal);\n\n tooltipSerieList.push({\n metric,\n color,\n value: rawVal === null ? undefined : rawVal,\n formattedValue,\n attributes,\n serie: s,\n });\n }\n\n return {\n tooltipSerieList,\n hasAttributes: hasAnyAttributes,\n hasMultipleMetrics: seenMetrics.size > 1,\n hasMultipleAttributes: seenAttributeKeys.size > 1,\n spansMultipleDays: checkIfSpansMultipleDays(xData, timeZone),\n };\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, LabeledMetricMetadata>,\n timeZone?: string,\n appearance?: React.ComponentType<TooltipProps>,\n series?: Serie[],\n visibilityLimit?: number,\n invertSort?: boolean,\n disableSuggestedLabel?: 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 actualIdx = findNearestNonNullIndex(u, idx);\n const timestamp = u.data[0][actualIdx];\n\n if (timestamp === undefined) {\n return;\n }\n\n const {\n tooltipSerieList,\n hasAttributes,\n hasMultipleAttributes,\n hasMultipleMetrics,\n spansMultipleDays,\n } = buildTooltipSerieList(\n u,\n actualIdx,\n formatValue,\n stacked,\n metadata,\n series,\n timeZone,\n );\n\n tooltipManager.show();\n\n const AppearanceTooltip = appearance;\n const tooltipElement = AppearanceTooltip ? (\n <AppearanceTooltip\n timestamp={timestamp}\n tooltipSerieList={tooltipSerieList}\n timeZone={timeZone}\n />\n ) : (\n <Tooltip\n timestamp={timestamp}\n tooltipSerieList={tooltipSerieList}\n timeZone={timeZone}\n spansMultipleDays={spansMultipleDays}\n stacked={stacked}\n hasAttributes={hasAttributes}\n hasMultipleAttributes={hasMultipleAttributes}\n hasMultipleMetrics={hasMultipleMetrics}\n visibilityLimit={visibilityLimit}\n invertSort={invertSort}\n disableSuggestedLabel={disableSuggestedLabel}\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\";\nimport { StackedData } from \"@/types\";\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 // Percentunit ranges from 0 to 1\n const percentMax = unit === \"percent\" ? 100 : 1;\n if (dataMax > percentMax) {\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, percentMax),\n ];\n }\n\n // Normal case\n return [\n usePredefinedIfExists(predefinedMin, 0),\n usePredefinedIfExists(predefinedMax, percentMax),\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 hideAxis?: boolean,\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: (self, _axisIdx, _scaleMin, _scaleMax, _plotDim) => {\n const height = self.height;\n if (height <= 100) {\n return 30; // At least 2 ticks, better if 3\n } if (height <= 150) {\n return 35; // Bettween 3 and 4 ticks\n } if (height <= 200) {\n return 45; // Between 3 and 4 ticks\n } else if (height <= 250) {\n return 55; // Between 3 and 4 ticks\n } if (height <= 300) {\n return 60; // Between 4 and 5 ticks\n } else {\n return 70; // At least 4 ticks\n }\n },\n show: !hideAxis,\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(data: uPlot.AlignedData, omit: boolean): StackedData {\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: Array<uPlot.AlignedData[number]> = [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 if (!omit) {\n for (let i = 1; i < data.length - 1; i++) {\n bands.push({\n series: [i + 1, i],\n });\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","hideAxis","hideCursor","relativeTimeAxis","invertSort","disableSuggestedLabel","parentContext","useContext","LoadingComponent","ErrorComponent","TooltipComponent","EmptyComponent","tooltipHide","tooltipVisibilityLimit","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","fill","hideAxis","hideCursor","relativeTimeAxis","invertSort","disableSuggestedLabel","UnblindClientProvider","Scope","useQuery","useMetrics","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","useQuery","res","metrics","useQuery","useMemo","ms","getTimeConfig","scope","props","timeRangeToCalculatedTimestamp","timeRange","now","ms","deduceTimestamp","startTime","endTime","calculatedStartTime","calculatedEndTime","useMemo","useTimeConfig","props","scope","useScope","timeRange","startTime","endTime","getTimeConfig","useTimeseries","params","apiBaseUrl","fetchImpl","useUnblindClientConfig","startTime","endTime","timeRange","useTimeConfig","queries","interval","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","params","apiBaseUrl","fetchImpl","useUnblindClientConfig","startTime","endTime","timeRange","useTimeConfig","query","useQuery","calculatedStartTime","calculatedEndTime","deduceTimestamp","endpoint","res","usage","useMemo","isLoading","hasError","useInfiniteQuery","useMemo","useLogs","props","apiBaseUrl","fetchImpl","useUnblindClientConfig","timeRange","startTime","endTime","useTimeConfig","attributes","body","severity","traceId","spanId","logId","query","useInfiniteQuery","pageParam","deducedStartTime","deducedEndTime","deduceTimestamp","res","data","nextPage","lastPage","useMemo","page","isLineThreshold","threshold","isRangeThreshold","isLabeledMetric","value","useEffect","useMemo","useEffect","useRef","uPlot","uPlot","getValueFormat","dateTimeFormat","dateTimeFormatTimeAgo","systemDateFormats","timeUnitSize","timeIncrs","formatTime","splits","foundIncr","range","timeZone","format","systemDateFormats","yearRoundedToDay","v","date","dateTimeFormat","showSeconds","showMillis","checkTimeStr","findBestIncrement","targetSeconds","scales","scale","mult","incr","lastScale","lastMult","generateRelativeXAxisSplits","u","min","max","generateRelativeXAxisValues","timestamp","index","dateTimeFormatTimeAgo","generateXAxisValues","approxTicks","increment","splitsInMs","s","createXAxisConfig","fontFamily","relativeTimeAxis","hideAxis","_","values","getChartFont","_self","_axisIdx","_scaleMin","_scaleMax","plotDim","computePosition","flip","offset","createRoot","TOOLTIP_DISTANCE_CURSOR","TOOLTIP_PADDING_FROM_CURSOR","TooltipManager","u","content","anchor","overlay","x","y","tooltipManager","dateTimeFormat","createContext","Fragment","useContext","jsx","jsxs","DEFAULT_VISIBILITY_LIMIT","SerieContext","useTooltipSerie","context","SerieProvider","serie","children","TooltipContext","useTooltip","TooltipProvider","formattedTime","tooltip","Divider","className","props","MetricsTooltip","unitCategory","tooltipSerieList","visibilityLimit","visibleSeries","afterLimit","Header","DateTime","UnitCategory","Content","x","Serie","Color","Metric","Value","Summary","MultipleAttributesTooltip","disableSuggestedLabel","firstSerie","i","Attributes","MultipleAttributesMultipleMetricsTooltip","series","formattedVal","allZeroes","allUndefined","invertSort","attributes","attributeValues","attributeValue","index","sortSeriesByValue","a","b","Tooltip","timestamp","unsortedTooltipSerieList","timeZone","spansMultipleDays","hasMultipleMetrics","hasAttributes","jsx","checkIfSpansMultipleDays","xData","timeZone","minTimestamp","maxTimestamp","minDate","maxDate","getDateString","date","findNearestNonNullIndex","u","idx","hasNonNullValue","seriesIndex","dataLength","distance","leftCandidate","rightCandidate","buildTooltipSerieList","actualIdx","formatValue","stacked","metadata","series","tooltipSerieList","seenMetrics","seenAttributeKeys","hasAnyAttributes","rawVal","prevVal","s","originalSerie","metric","attributes","key","stroke","color","formattedValue","tooltipPlugin","appearance","visibilityLimit","invertSort","disableSuggestedLabel","over","boundingLeft","boundingTop","isHovering","syncBounds","bbox","tooltipManager","left","top","timestamp","hasAttributes","hasMultipleAttributes","hasMultipleMetrics","spansMultipleDays","AppearanceTooltip","tooltipElement","Tooltip","DEFAULT_COLORS","getChartFont","fontFamiliy","compareAndResolveUnit","paramsUnit","metricMetadata","unit","isColorsObject","colors","isColorsArray","MAX_COLORS","getFillingColor","isFilling","useSolid","index","colorIndex","getColor","serie","colors","type","computedStyle","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","createChartOptions","container","metadata","timeZone","tooltipComponent","relativeTimeAxis","hideTooltip","predefinedMin","predefinedMax","hideAxis","hideCursor","visibilityLimit","invertSort","disableSuggestedLabel","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","percentMax","niceMax","niceMin","calculateYAxisSize","self","values","axisIdx","cycleNum","axis","axisSize","longestVal","acc","val","createYAxisConfig","formatter","fontFamiliy","hideAxis","getChartFont","vals","v","formmatedVal","_axisIdx","_scaleMin","_scaleMax","_plotDim","height","stack","data","omit","bands","xAxis","xAxisLen","accum","accumData","serie","i","value","index","b","jsx","Chart","props","times","series","metadata","type","className","timeZone","propsOptions","tooltip","propColors","tooltipComponent","propsUnit","propFill","thresholds","predefinedMin","predefinedMax","propHideAxis","propHideCursor","propRelativeTimeAxis","propInvertSort","propDisableSuggestedLabel","propHideTooltip","propVisibilityLimit","chartRef","useRef","scope","useScope","relativeTimeAxis","colors","fill","invertSort","disableSuggestedLabel","hideAxis","hideCursor","visibilityLimit","hideTooltip","useEffect","data","unit","serie","metricMetadata","compareAndResolveUnit","stacked","stackedData","stack","threshold","isLineThreshold","isRangeThreshold","container","u","opts","createChartOptions","uPlot","resizeObserver","jsx","Timeseries","metrics","propOperator","propAttributes","propGroupBy","propInterval","type","className","propAppearance","propTooltip","onResponse","chartStyleProps","scope","useScope","interval","attributes","groupBy","operator","tooltipComponent","tooltip","useMemo","queries","metadataLabels","normalizedMetrics","metric","name","isLabeledMetric","response","useTimeseries","isLoading","data","hasError","series","times","metadata","isEmpty","x","labeledMetadata","metricName","meta","containerClassName","useEffect","LoadingComponent","ErrorComponent","EmptyComponent","Chart"]}