diff --git a/apps/web/src/components/dashboard-builder/widgets/chart-preview.tsx b/apps/web/src/components/dashboard-builder/widgets/chart-preview.tsx
index 71c0cab..2a3375f 100644
--- a/apps/web/src/components/dashboard-builder/widgets/chart-preview.tsx
+++ b/apps/web/src/components/dashboard-builder/widgets/chart-preview.tsx
@@ -1,5 +1,5 @@
import { Suspense, type ComponentType } from "react"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
interface ChartPreviewProps {
component: ComponentType<{ className?: string }>
diff --git a/apps/web/src/components/dashboard-builder/widgets/chart-widget.tsx b/apps/web/src/components/dashboard-builder/widgets/chart-widget.tsx
index ff0f05b..8647ff7 100644
--- a/apps/web/src/components/dashboard-builder/widgets/chart-widget.tsx
+++ b/apps/web/src/components/dashboard-builder/widgets/chart-widget.tsx
@@ -1,7 +1,7 @@
import { memo, Suspense } from "react"
-import { Skeleton } from "@/components/ui/skeleton"
-import { getChartById } from "@/components/charts/registry"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { getChartById } from "@maple/ui/components/charts/registry"
import { WidgetShell } from "@/components/dashboard-builder/widgets/widget-shell"
import type {
WidgetDataState,
diff --git a/apps/web/src/components/dashboard-builder/widgets/stat-widget.tsx b/apps/web/src/components/dashboard-builder/widgets/stat-widget.tsx
index b7739d1..f08292f 100644
--- a/apps/web/src/components/dashboard-builder/widgets/stat-widget.tsx
+++ b/apps/web/src/components/dashboard-builder/widgets/stat-widget.tsx
@@ -1,5 +1,5 @@
import { memo } from "react"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { WidgetShell } from "@/components/dashboard-builder/widgets/widget-shell"
import type {
WidgetDataState,
diff --git a/apps/web/src/components/dashboard-builder/widgets/table-widget.tsx b/apps/web/src/components/dashboard-builder/widgets/table-widget.tsx
index 243bc14..212db51 100644
--- a/apps/web/src/components/dashboard-builder/widgets/table-widget.tsx
+++ b/apps/web/src/components/dashboard-builder/widgets/table-widget.tsx
@@ -1,5 +1,5 @@
import { memo } from "react"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import {
Table,
TableBody,
@@ -7,7 +7,7 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
+} from "@maple/ui/components/ui/table"
import { WidgetShell } from "@/components/dashboard-builder/widgets/widget-shell"
import type {
WidgetDataState,
diff --git a/apps/web/src/components/dashboard-builder/widgets/widget-edit-panel.tsx b/apps/web/src/components/dashboard-builder/widgets/widget-edit-panel.tsx
index 40943f3..b1616c6 100644
--- a/apps/web/src/components/dashboard-builder/widgets/widget-edit-panel.tsx
+++ b/apps/web/src/components/dashboard-builder/widgets/widget-edit-panel.tsx
@@ -1,5 +1,5 @@
-import { Input } from "@/components/ui/input"
-import { getChartById, getChartsByCategory } from "@/components/charts/registry"
+import { Input } from "@maple/ui/components/ui/input"
+import { getChartById, getChartsByCategory } from "@maple/ui/components/charts/registry"
import { ChartPreview } from "@/components/dashboard-builder/widgets/chart-preview"
import type {
DashboardWidget,
diff --git a/apps/web/src/components/dashboard-builder/widgets/widget-shell.tsx b/apps/web/src/components/dashboard-builder/widgets/widget-shell.tsx
index 8bc5990..eae7f90 100644
--- a/apps/web/src/components/dashboard-builder/widgets/widget-shell.tsx
+++ b/apps/web/src/components/dashboard-builder/widgets/widget-shell.tsx
@@ -1,9 +1,9 @@
import type { ReactNode } from "react"
import { GripDotsIcon, TrashIcon, GearIcon, PencilIcon } from "@/components/icons"
-import { Card, CardContent, CardHeader, CardTitle, CardAction } from "@/components/ui/card"
-import { Button } from "@/components/ui/button"
-import { Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverTitle } from "@/components/ui/popover"
+import { Card, CardContent, CardHeader, CardTitle, CardAction } from "@maple/ui/components/ui/card"
+import { Button } from "@maple/ui/components/ui/button"
+import { Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverTitle } from "@maple/ui/components/ui/popover"
import type { WidgetMode } from "@/components/dashboard-builder/types"
interface WidgetShellProps {
diff --git a/apps/web/src/components/dashboard/app-sidebar.tsx b/apps/web/src/components/dashboard/app-sidebar.tsx
index a48a8e8..d8968e2 100644
--- a/apps/web/src/components/dashboard/app-sidebar.tsx
+++ b/apps/web/src/components/dashboard/app-sidebar.tsx
@@ -20,7 +20,7 @@ import {
Collapsible,
CollapsibleTrigger,
CollapsibleContent,
-} from "@/components/ui/collapsible"
+} from "@maple/ui/components/ui/collapsible"
import { OrgSwitcher } from "@/components/dashboard/org-switcher"
import {
DropdownMenu,
@@ -30,7 +30,7 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
+} from "@maple/ui/components/ui/dropdown-menu"
import {
Sidebar,
SidebarContent,
@@ -43,7 +43,7 @@ import {
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
-} from "@/components/ui/sidebar"
+} from "@maple/ui/components/ui/sidebar"
import { isClerkAuthEnabled } from "@/lib/services/common/auth-mode"
import { clearSelfHostedSessionToken } from "@/lib/services/common/self-hosted-auth"
import { useQuickStart } from "@/hooks/use-quick-start"
diff --git a/apps/web/src/components/dashboard/metrics-grid.tsx b/apps/web/src/components/dashboard/metrics-grid.tsx
index e78570a..ddf8e66 100644
--- a/apps/web/src/components/dashboard/metrics-grid.tsx
+++ b/apps/web/src/components/dashboard/metrics-grid.tsx
@@ -1,12 +1,12 @@
import { Suspense } from "react"
-import { cn } from "@/lib/utils"
-import { Skeleton } from "@/components/ui/skeleton"
-import { getChartById } from "@/components/charts/registry"
+import { cn } from "@maple/ui/utils"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { getChartById } from "@maple/ui/components/charts/registry"
import type {
ChartLegendMode,
ChartTooltipMode,
-} from "@/components/charts/_shared/chart-types"
+} from "@maple/ui/components/charts/_shared/chart-types"
import { ReadonlyWidgetShell } from "@/components/dashboard-builder/widgets/widget-shell"
interface MetricsGridItem {
diff --git a/apps/web/src/components/dashboard/org-switcher.tsx b/apps/web/src/components/dashboard/org-switcher.tsx
index 54d8560..f8667fb 100644
--- a/apps/web/src/components/dashboard/org-switcher.tsx
+++ b/apps/web/src/components/dashboard/org-switcher.tsx
@@ -17,21 +17,21 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
+} from "@maple/ui/components/ui/dropdown-menu"
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
-} from "@/components/ui/sidebar"
+} from "@maple/ui/components/ui/sidebar"
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
-} from "@/components/ui/dialog"
-import { Input } from "@/components/ui/input"
-import { Button } from "@/components/ui/button"
+} from "@maple/ui/components/ui/dialog"
+import { Input } from "@maple/ui/components/ui/input"
+import { Button } from "@maple/ui/components/ui/button"
import { isClerkAuthEnabled } from "@/lib/services/common/auth-mode"
function OrgAvatar({
diff --git a/apps/web/src/components/dashboard/service-usage-cards.tsx b/apps/web/src/components/dashboard/service-usage-cards.tsx
index 12e0f15..e9f09b1 100644
--- a/apps/web/src/components/dashboard/service-usage-cards.tsx
+++ b/apps/web/src/components/dashboard/service-usage-cards.tsx
@@ -6,8 +6,8 @@ import {
DatabaseIcon,
} from "@/components/icons"
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Card, CardContent, CardHeader, CardTitle } from "@maple/ui/components/ui/card"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { getServiceUsageResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
function formatNumber(num: number): string {
diff --git a/apps/web/src/components/errors/errors-by-type-table.tsx b/apps/web/src/components/errors/errors-by-type-table.tsx
index 7b9f4e2..cf0b5ce 100644
--- a/apps/web/src/components/errors/errors-by-type-table.tsx
+++ b/apps/web/src/components/errors/errors-by-type-table.tsx
@@ -11,9 +11,9 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { Badge } from "@/components/ui/badge"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "@maple/ui/components/ui/table"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { type GetErrorsByTypeInput, type ErrorByType } from "@/api/tinybird/errors"
import { formatDuration } from "@/lib/format"
import {
diff --git a/apps/web/src/components/errors/errors-filter-sidebar.tsx b/apps/web/src/components/errors/errors-filter-sidebar.tsx
index ff462be..ca398b2 100644
--- a/apps/web/src/components/errors/errors-filter-sidebar.tsx
+++ b/apps/web/src/components/errors/errors-filter-sidebar.tsx
@@ -4,7 +4,7 @@ import { useNavigate } from "@tanstack/react-router"
import { useEffectiveTimeRange } from "@/hooks/use-effective-time-range"
import { FilterSection, SingleCheckboxFilter } from "@/components/traces/filter-section"
import { Route } from "@/routes/errors"
-import { Separator } from "@/components/ui/separator"
+import { Separator } from "@maple/ui/components/ui/separator"
import { getErrorsFacetsResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import {
FilterSidebarBody,
diff --git a/apps/web/src/components/errors/errors-summary-cards.tsx b/apps/web/src/components/errors/errors-summary-cards.tsx
index e146699..c86e36e 100644
--- a/apps/web/src/components/errors/errors-summary-cards.tsx
+++ b/apps/web/src/components/errors/errors-summary-cards.tsx
@@ -6,8 +6,8 @@ import {
PulseIcon,
} from "@/components/icons"
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Card, CardContent, CardHeader, CardTitle } from "@maple/ui/components/ui/card"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { type GetErrorsSummaryInput } from "@/api/tinybird/errors"
import { getErrorsSummaryResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
diff --git a/apps/web/src/components/example.tsx b/apps/web/src/components/example.tsx
index 7883492..8873388 100644
--- a/apps/web/src/components/example.tsx
+++ b/apps/web/src/components/example.tsx
@@ -1,4 +1,4 @@
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
function ExampleWrapper({ className, ...props }: React.ComponentProps<"div">) {
return (
diff --git a/apps/web/src/components/filters/filter-section.tsx b/apps/web/src/components/filters/filter-section.tsx
index 31ca9ad..0b1be2e 100644
--- a/apps/web/src/components/filters/filter-section.tsx
+++ b/apps/web/src/components/filters/filter-section.tsx
@@ -1,14 +1,14 @@
import * as React from "react"
import { ChevronDownIcon, XmarkIcon, MagnifierIcon } from "@/components/icons"
-import { cn } from "@/lib/utils"
-import { Checkbox } from "@/components/ui/checkbox"
-import { Label } from "@/components/ui/label"
+import { cn } from "@maple/ui/utils"
+import { Checkbox } from "@maple/ui/components/ui/checkbox"
+import { Label } from "@maple/ui/components/ui/label"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
-} from "@/components/ui/collapsible"
+} from "@maple/ui/components/ui/collapsible"
export interface FilterOption {
name: string
diff --git a/apps/web/src/components/filters/filter-sidebar.tsx b/apps/web/src/components/filters/filter-sidebar.tsx
index e63134e..f068f10 100644
--- a/apps/web/src/components/filters/filter-sidebar.tsx
+++ b/apps/web/src/components/filters/filter-sidebar.tsx
@@ -1,9 +1,9 @@
import type { ReactNode } from "react"
-import { Separator } from "@/components/ui/separator"
-import { Skeleton } from "@/components/ui/skeleton"
-import { ScrollArea } from "@/components/ui/scroll-area"
-import { cn } from "@/lib/utils"
+import { Separator } from "@maple/ui/components/ui/separator"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { ScrollArea } from "@maple/ui/components/ui/scroll-area"
+import { cn } from "@maple/ui/utils"
interface FilterSidebarFrameProps {
children: ReactNode
diff --git a/apps/web/src/components/layout/dashboard-layout.tsx b/apps/web/src/components/layout/dashboard-layout.tsx
index 8b33c2c..41b0747 100644
--- a/apps/web/src/components/layout/dashboard-layout.tsx
+++ b/apps/web/src/components/layout/dashboard-layout.tsx
@@ -1,8 +1,8 @@
import * as React from "react"
import { AppSidebar } from "@/components/dashboard/app-sidebar"
-import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
-import { Separator } from "@/components/ui/separator"
+import { SidebarInset, SidebarProvider, SidebarTrigger } from "@maple/ui/components/ui/sidebar"
+import { Separator } from "@maple/ui/components/ui/separator"
import {
Breadcrumb,
BreadcrumbItem,
@@ -10,7 +10,7 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
-} from "@/components/ui/breadcrumb"
+} from "@maple/ui/components/ui/breadcrumb"
import { Link } from "@tanstack/react-router"
export interface BreadcrumbItem {
diff --git a/apps/web/src/components/logs/log-detail-sheet.tsx b/apps/web/src/components/logs/log-detail-sheet.tsx
index a54350c..0d1507f 100644
--- a/apps/web/src/components/logs/log-detail-sheet.tsx
+++ b/apps/web/src/components/logs/log-detail-sheet.tsx
@@ -7,12 +7,12 @@ import {
SheetContent,
SheetTitle,
SheetClose,
-} from "@/components/ui/sheet"
-import { Badge } from "@/components/ui/badge"
-import { Button } from "@/components/ui/button"
-import { ScrollArea } from "@/components/ui/scroll-area"
+} from "@maple/ui/components/ui/sheet"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Button } from "@maple/ui/components/ui/button"
+import { ScrollArea } from "@maple/ui/components/ui/scroll-area"
import { SeverityBadge } from "./severity-badge"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import type { Log } from "@/api/tinybird/logs"
interface LogDetailSheetProps {
diff --git a/apps/web/src/components/logs/logs-filter-sidebar.tsx b/apps/web/src/components/logs/logs-filter-sidebar.tsx
index 2f04ab0..ac4f87e 100644
--- a/apps/web/src/components/logs/logs-filter-sidebar.tsx
+++ b/apps/web/src/components/logs/logs-filter-sidebar.tsx
@@ -9,7 +9,7 @@ import {
SearchableFilterSection,
} from "@/components/filters/filter-section"
import { Route } from "@/routes/logs"
-import { Separator } from "@/components/ui/separator"
+import { Separator } from "@maple/ui/components/ui/separator"
import { getLogsFacetsResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import {
FilterSidebarBody,
diff --git a/apps/web/src/components/logs/logs-table.tsx b/apps/web/src/components/logs/logs-table.tsx
index 45dccf6..cc7d883 100644
--- a/apps/web/src/components/logs/logs-table.tsx
+++ b/apps/web/src/components/logs/logs-table.tsx
@@ -8,8 +8,8 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "@maple/ui/components/ui/table"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { type Log } from "@/api/tinybird/logs"
import { useEffectiveTimeRange } from "@/hooks/use-effective-time-range"
import { SeverityBadge } from "./severity-badge"
diff --git a/apps/web/src/components/logs/severity-badge.tsx b/apps/web/src/components/logs/severity-badge.tsx
index f1738e2..d7698b7 100644
--- a/apps/web/src/components/logs/severity-badge.tsx
+++ b/apps/web/src/components/logs/severity-badge.tsx
@@ -1,5 +1,5 @@
-import { Badge } from "@/components/ui/badge"
-import { cn } from "@/lib/utils"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { cn } from "@maple/ui/utils"
interface SeverityBadgeProps {
severity: string
diff --git a/apps/web/src/components/metrics/metric-type-badge.tsx b/apps/web/src/components/metrics/metric-type-badge.tsx
index 97aadbe..f538e68 100644
--- a/apps/web/src/components/metrics/metric-type-badge.tsx
+++ b/apps/web/src/components/metrics/metric-type-badge.tsx
@@ -1,4 +1,4 @@
-import { Badge } from "@/components/ui/badge"
+import { Badge } from "@maple/ui/components/ui/badge"
const metricTypeConfig: Record
= {
sum: {
diff --git a/apps/web/src/components/metrics/metrics-overview.tsx b/apps/web/src/components/metrics/metrics-overview.tsx
index de497d4..fe9a388 100644
--- a/apps/web/src/components/metrics/metrics-overview.tsx
+++ b/apps/web/src/components/metrics/metrics-overview.tsx
@@ -1,6 +1,6 @@
import { useState } from "react"
-import { Input } from "@/components/ui/input"
+import { Input } from "@maple/ui/components/ui/input"
import { MetricsSummaryCards, type MetricType } from "./metrics-summary-cards"
import { MetricsVolumeChart } from "./metrics-volume-chart"
import { MetricsTable } from "./metrics-table"
diff --git a/apps/web/src/components/metrics/metrics-summary-cards.tsx b/apps/web/src/components/metrics/metrics-summary-cards.tsx
index 033568b..5cfde4d 100644
--- a/apps/web/src/components/metrics/metrics-summary-cards.tsx
+++ b/apps/web/src/components/metrics/metrics-summary-cards.tsx
@@ -6,8 +6,8 @@ import {
ChartBarTrendUpIcon,
} from "@/components/icons"
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Card, CardContent, CardHeader, CardTitle } from "@maple/ui/components/ui/card"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { type ListMetricsInput } from "@/api/tinybird/metrics"
import { getMetricsSummaryResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
diff --git a/apps/web/src/components/metrics/metrics-table.tsx b/apps/web/src/components/metrics/metrics-table.tsx
index e19841f..7c6d2ee 100644
--- a/apps/web/src/components/metrics/metrics-table.tsx
+++ b/apps/web/src/components/metrics/metrics-table.tsx
@@ -7,9 +7,9 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { Badge } from "@/components/ui/badge"
+} from "@maple/ui/components/ui/table"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { Badge } from "@maple/ui/components/ui/badge"
import { MetricTypeBadge } from "./metric-type-badge"
import { type Metric, type ListMetricsInput } from "@/api/tinybird/metrics"
import { listMetricsResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
diff --git a/apps/web/src/components/metrics/metrics-volume-chart.tsx b/apps/web/src/components/metrics/metrics-volume-chart.tsx
index 82961cb..5aa0be6 100644
--- a/apps/web/src/components/metrics/metrics-volume-chart.tsx
+++ b/apps/web/src/components/metrics/metrics-volume-chart.tsx
@@ -6,9 +6,9 @@ import {
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
-} from "@/components/ui/chart"
-import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "@maple/ui/components/ui/chart"
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@maple/ui/components/ui/card"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { type GetMetricTimeSeriesInput, type MetricTimeSeriesResponse } from "@/api/tinybird/metrics"
import { disabledResultAtom } from "@/lib/services/atoms/disabled-result-atom"
import { getMetricTimeSeriesResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
diff --git a/apps/web/src/components/query-builder/query-builder-lab.tsx b/apps/web/src/components/query-builder/query-builder-lab.tsx
index 46af3f2..1519733 100644
--- a/apps/web/src/components/query-builder/query-builder-lab.tsx
+++ b/apps/web/src/components/query-builder/query-builder-lab.tsx
@@ -2,8 +2,8 @@ import * as React from "react"
import { Result, useAtomValue } from "@effect-atom/atom-react"
import { PulseIcon, XmarkIcon, PlusIcon, MagnifierIcon } from "@/components/icons"
-import { Badge } from "@/components/ui/badge"
-import { Button } from "@/components/ui/button"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Button } from "@maple/ui/components/ui/button"
import {
Card,
CardContent,
@@ -11,19 +11,19 @@ import {
CardFooter,
CardHeader,
CardTitle,
-} from "@/components/ui/card"
-import { Checkbox } from "@/components/ui/checkbox"
-import { Input } from "@/components/ui/input"
-import { Label } from "@/components/ui/label"
-import { ScrollArea } from "@/components/ui/scroll-area"
-import { Separator } from "@/components/ui/separator"
+} from "@maple/ui/components/ui/card"
+import { Checkbox } from "@maple/ui/components/ui/checkbox"
+import { Input } from "@maple/ui/components/ui/input"
+import { Label } from "@maple/ui/components/ui/label"
+import { ScrollArea } from "@maple/ui/components/ui/scroll-area"
+import { Separator } from "@maple/ui/components/ui/separator"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
-} from "@/components/ui/select"
+} from "@maple/ui/components/ui/select"
import {
Table,
TableBody,
@@ -31,8 +31,8 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { cn } from "@/lib/utils"
+} from "@maple/ui/components/ui/table"
+import { cn } from "@maple/ui/utils"
import { WhereClauseEditor } from "@/components/query-builder/where-clause-editor"
import {
getLogsFacetsResultAtom,
diff --git a/apps/web/src/components/query-builder/where-clause-editor.tsx b/apps/web/src/components/query-builder/where-clause-editor.tsx
index 52271b7..cfdc14a 100644
--- a/apps/web/src/components/query-builder/where-clause-editor.tsx
+++ b/apps/web/src/components/query-builder/where-clause-editor.tsx
@@ -1,13 +1,13 @@
import * as React from "react"
-import { Textarea } from "@/components/ui/textarea"
+import { Textarea } from "@maple/ui/components/ui/textarea"
import {
applyWhereClauseSuggestion,
getWhereClauseAutocomplete,
type WhereClauseAutocompleteValues,
} from "@/lib/query-builder/where-clause-autocomplete"
import type { QueryBuilderDataSource } from "@/lib/query-builder/model"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
interface WhereClauseEditorProps {
dataSource: QueryBuilderDataSource
diff --git a/apps/web/src/components/quick-start/code-block.tsx b/apps/web/src/components/quick-start/code-block.tsx
index 642c0af..cd398cd 100644
--- a/apps/web/src/components/quick-start/code-block.tsx
+++ b/apps/web/src/components/quick-start/code-block.tsx
@@ -1,7 +1,7 @@
import { useState } from "react"
import { toast } from "sonner"
import { CopyIcon, CheckIcon } from "@/components/icons"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import { highlightCode } from "@/lib/sugar-high"
interface CodeBlockProps {
diff --git a/apps/web/src/components/quick-start/package-manager-code-block.tsx b/apps/web/src/components/quick-start/package-manager-code-block.tsx
index d7c14f8..bf0fb9f 100644
--- a/apps/web/src/components/quick-start/package-manager-code-block.tsx
+++ b/apps/web/src/components/quick-start/package-manager-code-block.tsx
@@ -1,4 +1,4 @@
-import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
+import { Tabs, TabsList, TabsTrigger, TabsContent } from "@maple/ui/components/ui/tabs"
import { CodeBlock } from "@/components/quick-start/code-block"
const packageManagers = [
diff --git a/apps/web/src/components/route-error.tsx b/apps/web/src/components/route-error.tsx
index f6728f2..fad1c45 100644
--- a/apps/web/src/components/route-error.tsx
+++ b/apps/web/src/components/route-error.tsx
@@ -1,14 +1,14 @@
import type { ErrorComponentProps } from "@tanstack/react-router"
import { Link, useRouter } from "@tanstack/react-router"
import { AlertWarningIcon, CircleQuestionIcon, HouseIcon } from "@/components/icons"
-import { Button, buttonVariants } from "@/components/ui/button"
+import { Button, buttonVariants } from "@maple/ui/components/ui/button"
import {
Empty,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
-} from "@/components/ui/empty"
+} from "@maple/ui/components/ui/empty"
function RouteError({ error, reset }: ErrorComponentProps) {
const router = useRouter()
diff --git a/apps/web/src/components/service-map/service-map-edge.tsx b/apps/web/src/components/service-map/service-map-edge.tsx
index 9fd5cf8..7c3cfed 100644
--- a/apps/web/src/components/service-map/service-map-edge.tsx
+++ b/apps/web/src/components/service-map/service-map-edge.tsx
@@ -1,6 +1,6 @@
import { memo, useId } from "react"
import { getSmoothStepPath, type EdgeProps } from "@xyflow/react"
-import { getServiceLegendColor } from "@/lib/colors"
+import { getServiceLegendColor } from "@maple/ui/colors"
import { useReducedMotion } from "@/hooks/use-reduced-motion"
import type { ServiceEdgeData } from "./service-map-utils"
diff --git a/apps/web/src/components/service-map/service-map-node.tsx b/apps/web/src/components/service-map/service-map-node.tsx
index 2be668c..3a99fa4 100644
--- a/apps/web/src/components/service-map/service-map-node.tsx
+++ b/apps/web/src/components/service-map/service-map-node.tsx
@@ -1,12 +1,12 @@
import { memo } from "react"
import { Handle, Position } from "@xyflow/react"
-import { cn } from "@/lib/utils"
-import { getServiceLegendColor } from "@/lib/colors"
+import { cn } from "@maple/ui/utils"
+import { getServiceLegendColor } from "@maple/ui/colors"
import {
Tooltip,
TooltipTrigger,
TooltipContent,
-} from "@/components/ui/tooltip"
+} from "@maple/ui/components/ui/tooltip"
import type { ServiceNodeData } from "./service-map-utils"
function formatRate(value: number): string {
diff --git a/apps/web/src/components/service-map/service-map-view.tsx b/apps/web/src/components/service-map/service-map-view.tsx
index c65d38a..5a7e264 100644
--- a/apps/web/src/components/service-map/service-map-view.tsx
+++ b/apps/web/src/components/service-map/service-map-view.tsx
@@ -14,7 +14,7 @@ import "@xyflow/react/dist/style.css"
import { Result, useAtomValue } from "@effect-atom/atom-react"
-import { getServiceLegendColor } from "@/lib/colors"
+import { getServiceLegendColor } from "@maple/ui/colors"
import { getServiceMapResultAtom, getServiceOverviewResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import type { GetServiceMapInput, ServiceEdge } from "@/api/tinybird/service-map"
import type { GetServiceOverviewInput, ServiceOverview } from "@/api/tinybird/services"
diff --git a/apps/web/src/components/services/services-filter-sidebar.tsx b/apps/web/src/components/services/services-filter-sidebar.tsx
index d00c6b3..8f65750 100644
--- a/apps/web/src/components/services/services-filter-sidebar.tsx
+++ b/apps/web/src/components/services/services-filter-sidebar.tsx
@@ -4,7 +4,7 @@ import { useNavigate } from "@tanstack/react-router"
import { useEffectiveTimeRange } from "@/hooks/use-effective-time-range"
import { FilterSection } from "@/components/traces/filter-section"
import { Route } from "@/routes/services/index"
-import { Separator } from "@/components/ui/separator"
+import { Separator } from "@maple/ui/components/ui/separator"
import { getServicesFacetsResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import {
FilterSidebarBody,
diff --git a/apps/web/src/components/services/services-table.tsx b/apps/web/src/components/services/services-table.tsx
index 239695b..9731a2a 100644
--- a/apps/web/src/components/services/services-table.tsx
+++ b/apps/web/src/components/services/services-table.tsx
@@ -9,15 +9,15 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { Badge } from "@/components/ui/badge"
-import { Skeleton } from "@/components/ui/skeleton"
-import { Sparkline } from "@/components/ui/gradient-chart"
+} from "@maple/ui/components/ui/table"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { Sparkline } from "@maple/ui/components/ui/gradient-chart"
import {
Tooltip,
TooltipTrigger,
TooltipContent,
-} from "@/components/ui/tooltip"
+} from "@maple/ui/components/ui/tooltip"
import {
type ServiceOverview,
type CommitBreakdown,
diff --git a/apps/web/src/components/settings/api-keys-section.tsx b/apps/web/src/components/settings/api-keys-section.tsx
index 3632d66..4d3175e 100644
--- a/apps/web/src/components/settings/api-keys-section.tsx
+++ b/apps/web/src/components/settings/api-keys-section.tsx
@@ -3,17 +3,17 @@ import { useState } from "react"
import { Exit } from "effect"
import { toast } from "sonner"
-import { Button } from "@/components/ui/button"
+import { Button } from "@maple/ui/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
-} from "@/components/ui/card"
-import { Badge } from "@/components/ui/badge"
-import { Input } from "@/components/ui/input"
-import { Label } from "@/components/ui/label"
+} from "@maple/ui/components/ui/card"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Input } from "@maple/ui/components/ui/input"
+import { Label } from "@maple/ui/components/ui/label"
import {
Dialog,
DialogContent,
@@ -21,7 +21,7 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
-} from "@/components/ui/dialog"
+} from "@maple/ui/components/ui/dialog"
import {
AlertDialog,
AlertDialogAction,
@@ -32,21 +32,21 @@ import {
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
-} from "@/components/ui/alert-dialog"
+} from "@maple/ui/components/ui/alert-dialog"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
-} from "@/components/ui/input-group"
+} from "@maple/ui/components/ui/input-group"
import {
Empty,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
-} from "@/components/ui/empty"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "@maple/ui/components/ui/empty"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import {
AlertWarningIcon,
CheckIcon,
diff --git a/apps/web/src/components/settings/billing-section.tsx b/apps/web/src/components/settings/billing-section.tsx
index e98c13b..fbd8a44 100644
--- a/apps/web/src/components/settings/billing-section.tsx
+++ b/apps/web/src/components/settings/billing-section.tsx
@@ -4,8 +4,8 @@ import { useCustomer } from "autumn-js/react"
import { PricingCards } from "./pricing-cards"
import { format } from "date-fns"
-import { Skeleton } from "@/components/ui/skeleton"
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { Card, CardContent, CardHeader, CardTitle } from "@maple/ui/components/ui/card"
import { getServiceUsageResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import { formatForTinybird } from "@/lib/time-utils"
import { aggregateUsage } from "@/lib/billing/usage"
diff --git a/apps/web/src/components/settings/members-section.tsx b/apps/web/src/components/settings/members-section.tsx
index a6dba8b..c26e83d 100644
--- a/apps/web/src/components/settings/members-section.tsx
+++ b/apps/web/src/components/settings/members-section.tsx
@@ -2,14 +2,14 @@ import { useOrganization, useAuth } from "@clerk/clerk-react"
import { useState } from "react"
import { toast } from "sonner"
-import { Button } from "@/components/ui/button"
+import { Button } from "@maple/ui/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
-} from "@/components/ui/card"
+} from "@maple/ui/components/ui/card"
import {
Table,
TableBody,
@@ -17,9 +17,9 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
-import { Badge } from "@/components/ui/badge"
+} from "@maple/ui/components/ui/table"
+import { Avatar, AvatarFallback, AvatarImage } from "@maple/ui/components/ui/avatar"
+import { Badge } from "@maple/ui/components/ui/badge"
import {
Dialog,
DialogContent,
@@ -27,22 +27,22 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
-} from "@/components/ui/dialog"
-import { Input } from "@/components/ui/input"
-import { Label } from "@/components/ui/label"
+} from "@maple/ui/components/ui/dialog"
+import { Input } from "@maple/ui/components/ui/input"
+import { Label } from "@maple/ui/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
-} from "@/components/ui/select"
+} from "@maple/ui/components/ui/select"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
+} from "@maple/ui/components/ui/dropdown-menu"
import {
AlertDialog,
AlertDialogAction,
@@ -53,15 +53,15 @@ import {
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
-} from "@/components/ui/alert-dialog"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "@maple/ui/components/ui/alert-dialog"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import {
Empty,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
-} from "@/components/ui/empty"
+} from "@maple/ui/components/ui/empty"
import {
PlusIcon,
DotsVerticalIcon,
diff --git a/apps/web/src/components/settings/pricing-cards.tsx b/apps/web/src/components/settings/pricing-cards.tsx
index 62d3ffd..f24245d 100644
--- a/apps/web/src/components/settings/pricing-cards.tsx
+++ b/apps/web/src/components/settings/pricing-cards.tsx
@@ -7,7 +7,7 @@ type Product = NonNullable<
>[number]
type ProductItem = Product["items"][number]
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import { getPlanFeatures } from "@/lib/billing/plans"
import {
Card,
@@ -16,12 +16,12 @@ import {
CardHeader,
CardTitle,
CardDescription,
-} from "@/components/ui/card"
-import { Button } from "@/components/ui/button"
-import { Badge } from "@/components/ui/badge"
-import { Separator } from "@/components/ui/separator"
-import { Skeleton } from "@/components/ui/skeleton"
-import { Spinner } from "@/components/ui/spinner"
+} from "@maple/ui/components/ui/card"
+import { Button } from "@maple/ui/components/ui/button"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Separator } from "@maple/ui/components/ui/separator"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { Spinner } from "@maple/ui/components/ui/spinner"
import {
Dialog,
DialogContent,
@@ -29,7 +29,7 @@ import {
DialogTitle,
DialogDescription,
DialogFooter,
-} from "@/components/ui/dialog"
+} from "@maple/ui/components/ui/dialog"
import {
FileIcon,
PulseIcon,
diff --git a/apps/web/src/components/settings/scrape-targets-section.tsx b/apps/web/src/components/settings/scrape-targets-section.tsx
index 4296c39..940cb2e 100644
--- a/apps/web/src/components/settings/scrape-targets-section.tsx
+++ b/apps/web/src/components/settings/scrape-targets-section.tsx
@@ -12,15 +12,15 @@ import {
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
-} from "@/components/ui/alert-dialog"
-import { Button } from "@/components/ui/button"
+} from "@maple/ui/components/ui/alert-dialog"
+import { Button } from "@maple/ui/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
-} from "@/components/ui/card"
+} from "@maple/ui/components/ui/card"
import {
Dialog,
DialogContent,
@@ -28,18 +28,18 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
-} from "@/components/ui/dialog"
-import { Input } from "@/components/ui/input"
-import { Label } from "@/components/ui/label"
-import { Switch } from "@/components/ui/switch"
-import { Badge } from "@/components/ui/badge"
+} from "@maple/ui/components/ui/dialog"
+import { Input } from "@maple/ui/components/ui/input"
+import { Label } from "@maple/ui/components/ui/label"
+import { Switch } from "@maple/ui/components/ui/switch"
+import { Badge } from "@maple/ui/components/ui/badge"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
-} from "@/components/ui/select"
+} from "@maple/ui/components/ui/select"
import {
CircleCheckIcon,
CircleXmarkIcon,
diff --git a/apps/web/src/components/settings/usage-meters.tsx b/apps/web/src/components/settings/usage-meters.tsx
index 464726f..8cef8a8 100644
--- a/apps/web/src/components/settings/usage-meters.tsx
+++ b/apps/web/src/components/settings/usage-meters.tsx
@@ -6,7 +6,7 @@ import {
CardDescription,
CardHeader,
CardTitle,
-} from "@/components/ui/card"
+} from "@maple/ui/components/ui/card"
import {
FileIcon,
PulseIcon,
@@ -16,7 +16,7 @@ import {
import type { AggregatedUsage } from "@/lib/billing/usage"
import { formatGB, usagePercentage } from "@/lib/billing/usage"
import type { PlanLimits } from "@/lib/billing/plans"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
interface MeterRowProps {
icon: IconComponent
diff --git a/apps/web/src/components/time-range-picker/custom-range-picker.tsx b/apps/web/src/components/time-range-picker/custom-range-picker.tsx
index 7322de0..94a1ade 100644
--- a/apps/web/src/components/time-range-picker/custom-range-picker.tsx
+++ b/apps/web/src/components/time-range-picker/custom-range-picker.tsx
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react"
-import { Calendar } from "@/components/ui/calendar"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
+import { Calendar } from "@maple/ui/components/ui/calendar"
+import { Button } from "@maple/ui/components/ui/button"
+import { Input } from "@maple/ui/components/ui/input"
import { format, parse, isValid, setHours, setMinutes } from "date-fns"
import type { DateRange } from "react-day-picker"
import { formatForTinybird } from "@/lib/time-utils"
diff --git a/apps/web/src/components/time-range-picker/preset-list.tsx b/apps/web/src/components/time-range-picker/preset-list.tsx
index 46c68de..649b2c5 100644
--- a/apps/web/src/components/time-range-picker/preset-list.tsx
+++ b/apps/web/src/components/time-range-picker/preset-list.tsx
@@ -1,5 +1,5 @@
import { PRESET_OPTIONS, type TimePreset } from "@/lib/time-utils"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
interface PresetListProps {
selectedValue?: string
diff --git a/apps/web/src/components/time-range-picker/quick-select-grid.tsx b/apps/web/src/components/time-range-picker/quick-select-grid.tsx
index 8ce2652..632ba58 100644
--- a/apps/web/src/components/time-range-picker/quick-select-grid.tsx
+++ b/apps/web/src/components/time-range-picker/quick-select-grid.tsx
@@ -1,5 +1,5 @@
import { QUICK_SELECT_OPTIONS, relativeToAbsolute } from "@/lib/time-utils"
-import { Button } from "@/components/ui/button"
+import { Button } from "@maple/ui/components/ui/button"
interface QuickSelectGridProps {
onSelect: (range: { startTime: string; endTime: string }, value: string, label: string) => void
diff --git a/apps/web/src/components/time-range-picker/recently-used.tsx b/apps/web/src/components/time-range-picker/recently-used.tsx
index 2839fd3..a4bd17e 100644
--- a/apps/web/src/components/time-range-picker/recently-used.tsx
+++ b/apps/web/src/components/time-range-picker/recently-used.tsx
@@ -1,5 +1,5 @@
import type { RecentTimeRange } from "@/hooks/use-recently-used-times"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
interface RecentlyUsedProps {
recentTimes: RecentTimeRange[]
diff --git a/apps/web/src/components/time-range-picker/shorthand-input.tsx b/apps/web/src/components/time-range-picker/shorthand-input.tsx
index 9df1d93..83af032 100644
--- a/apps/web/src/components/time-range-picker/shorthand-input.tsx
+++ b/apps/web/src/components/time-range-picker/shorthand-input.tsx
@@ -1,5 +1,5 @@
import { useState, useCallback } from "react"
-import { Input } from "@/components/ui/input"
+import { Input } from "@maple/ui/components/ui/input"
import { relativeToAbsolute } from "@/lib/time-utils"
interface ShorthandInputProps {
diff --git a/apps/web/src/components/time-range-picker/time-range-picker.tsx b/apps/web/src/components/time-range-picker/time-range-picker.tsx
index 0cd4771..2d4d825 100644
--- a/apps/web/src/components/time-range-picker/time-range-picker.tsx
+++ b/apps/web/src/components/time-range-picker/time-range-picker.tsx
@@ -1,8 +1,8 @@
import { useState, useCallback } from "react"
-import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
-import { Button } from "@/components/ui/button"
-import { ScrollArea } from "@/components/ui/scroll-area"
-import { Separator } from "@/components/ui/separator"
+import { Popover, PopoverContent, PopoverTrigger } from "@maple/ui/components/ui/popover"
+import { Button } from "@maple/ui/components/ui/button"
+import { ScrollArea } from "@maple/ui/components/ui/scroll-area"
+import { Separator } from "@maple/ui/components/ui/separator"
import { ClockIcon } from "@/components/icons"
import { formatTimeRangeDisplay, presetLabel, type TimePreset, relativeToAbsolute } from "@/lib/time-utils"
diff --git a/apps/web/src/components/traces/duration-range-filter.tsx b/apps/web/src/components/traces/duration-range-filter.tsx
index 0e3f71b..00fc770 100644
--- a/apps/web/src/components/traces/duration-range-filter.tsx
+++ b/apps/web/src/components/traces/duration-range-filter.tsx
@@ -1,14 +1,14 @@
import * as React from "react"
import { ChevronDownIcon } from "@/components/icons"
-import { cn } from "@/lib/utils"
-import { Input } from "@/components/ui/input"
-import { Label } from "@/components/ui/label"
+import { cn } from "@maple/ui/utils"
+import { Input } from "@maple/ui/components/ui/input"
+import { Label } from "@maple/ui/components/ui/label"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
-} from "@/components/ui/collapsible"
+} from "@maple/ui/components/ui/collapsible"
interface DurationRangeFilterProps {
minValue: number | undefined
diff --git a/apps/web/src/components/traces/flamegraph-minimap.tsx b/apps/web/src/components/traces/flamegraph-minimap.tsx
index ba344bf..30a83a0 100644
--- a/apps/web/src/components/traces/flamegraph-minimap.tsx
+++ b/apps/web/src/components/traces/flamegraph-minimap.tsx
@@ -1,5 +1,5 @@
import * as React from "react"
-import { getSpanColorStyle } from "@/lib/colors"
+import { getSpanColorStyle } from "@maple/ui/colors"
import type { SpanNode } from "@/api/tinybird/traces"
interface FlamegraphMinimapProps {
diff --git a/apps/web/src/components/traces/flamegraph-tooltip.tsx b/apps/web/src/components/traces/flamegraph-tooltip.tsx
index cfacd80..f981693 100644
--- a/apps/web/src/components/traces/flamegraph-tooltip.tsx
+++ b/apps/web/src/components/traces/flamegraph-tooltip.tsx
@@ -1,5 +1,5 @@
import { formatDuration } from "@/lib/format"
-import { getServiceLegendColor, calculateSelfTime } from "@/lib/colors"
+import { getServiceLegendColor, calculateSelfTime } from "@maple/ui/colors"
import type { SpanNode } from "@/api/tinybird/traces"
interface FlamegraphTooltipProps {
diff --git a/apps/web/src/components/traces/flamegraph.tsx b/apps/web/src/components/traces/flamegraph.tsx
index 6d2c51f..11b855b 100644
--- a/apps/web/src/components/traces/flamegraph.tsx
+++ b/apps/web/src/components/traces/flamegraph.tsx
@@ -2,13 +2,13 @@ import * as React from "react"
import { XmarkIcon } from "@/components/icons"
-import { Button } from "@/components/ui/button"
-import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"
+import { Button } from "@maple/ui/components/ui/button"
+import { Tooltip, TooltipTrigger, TooltipContent } from "@maple/ui/components/ui/tooltip"
import { FlamegraphTooltipContent } from "./flamegraph-tooltip"
import { FlamegraphMinimap } from "./flamegraph-minimap"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import { formatDuration } from "@/lib/format"
-import { getSpanColorStyle, getServiceLegendColor } from "@/lib/colors"
+import { getSpanColorStyle, getServiceLegendColor } from "@maple/ui/colors"
import { getCacheInfo } from "@/lib/cache"
import type { SpanNode } from "@/api/tinybird/traces"
diff --git a/apps/web/src/components/traces/flow-node.tsx b/apps/web/src/components/traces/flow-node.tsx
index dba3ccc..0d597ff 100644
--- a/apps/web/src/components/traces/flow-node.tsx
+++ b/apps/web/src/components/traces/flow-node.tsx
@@ -11,9 +11,9 @@ import {
} from "@/components/icons"
import type { IconComponent } from "@/components/icons"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import { formatDuration } from "@/lib/format"
-import { getSpanColorStyle, extractClassName } from "@/lib/colors"
+import { getSpanColorStyle, extractClassName } from "@maple/ui/colors"
import { getCacheInfo, cacheResultStyles, CACHE_OPERATION_COLORS } from "@/lib/cache"
import type { CacheInfo } from "@/lib/cache"
import type { FlowNodeData, AggregatedDuration } from "./flow-utils"
diff --git a/apps/web/src/components/traces/flow-view.tsx b/apps/web/src/components/traces/flow-view.tsx
index 645a315..a4d87f5 100644
--- a/apps/web/src/components/traces/flow-view.tsx
+++ b/apps/web/src/components/traces/flow-view.tsx
@@ -13,8 +13,8 @@ import "@xyflow/react/dist/style.css"
import { EyeIcon } from "@/components/icons"
-import { Button } from "@/components/ui/button"
-import { getServiceLegendColor } from "@/lib/colors"
+import { Button } from "@maple/ui/components/ui/button"
+import { getServiceLegendColor } from "@maple/ui/colors"
import { FlowSpanNode } from "./flow-node"
import {
transformSpansToFlow,
diff --git a/apps/web/src/components/traces/span-detail-panel.tsx b/apps/web/src/components/traces/span-detail-panel.tsx
index fed2128..88dc439 100644
--- a/apps/web/src/components/traces/span-detail-panel.tsx
+++ b/apps/web/src/components/traces/span-detail-panel.tsx
@@ -3,16 +3,16 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"
import { XmarkIcon, ClockIcon, CircleWarningIcon, ChevronDownIcon, ChevronUpIcon, CopyIcon } from "@/components/icons"
import { toast } from "sonner"
-import { Button } from "@/components/ui/button"
-import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert"
-import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible"
-import { Badge } from "@/components/ui/badge"
-import { Skeleton } from "@/components/ui/skeleton"
-import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
-import { ScrollArea } from "@/components/ui/scroll-area"
+import { Button } from "@maple/ui/components/ui/button"
+import { Alert, AlertTitle, AlertDescription } from "@maple/ui/components/ui/alert"
+import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@maple/ui/components/ui/collapsible"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
+import { Tabs, TabsList, TabsTrigger, TabsContent } from "@maple/ui/components/ui/tabs"
+import { ScrollArea } from "@maple/ui/components/ui/scroll-area"
import { type Log, type LogsResponse } from "@/api/tinybird/logs"
import { formatDuration } from "@/lib/format"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import { getCacheInfo, cacheResultStyles } from "@/lib/cache"
import type { SpanNode } from "@/api/tinybird/traces"
import { disabledResultAtom } from "@/lib/services/atoms/disabled-result-atom"
diff --git a/apps/web/src/components/traces/span-row.tsx b/apps/web/src/components/traces/span-row.tsx
index 65004cb..df903a0 100644
--- a/apps/web/src/components/traces/span-row.tsx
+++ b/apps/web/src/components/traces/span-row.tsx
@@ -1,8 +1,8 @@
import { ChevronRightIcon, ChevronDownIcon } from "@/components/icons"
-import { Badge } from "@/components/ui/badge"
-import { Button } from "@/components/ui/button"
-import { cn } from "@/lib/utils"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Button } from "@maple/ui/components/ui/button"
+import { cn } from "@maple/ui/utils"
import { formatDuration } from "@/lib/format"
import { getCacheInfo, cacheResultStyles } from "@/lib/cache"
import type { SpanNode } from "@/api/tinybird/traces"
diff --git a/apps/web/src/components/traces/trace-view-tabs.tsx b/apps/web/src/components/traces/trace-view-tabs.tsx
index 20e9040..730e951 100644
--- a/apps/web/src/components/traces/trace-view-tabs.tsx
+++ b/apps/web/src/components/traces/trace-view-tabs.tsx
@@ -1,6 +1,6 @@
import { MenuIcon, FireIcon, NetworkNodesIcon } from "@/components/icons"
-import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
+import { Tabs, TabsList, TabsTrigger, TabsContent } from "@maple/ui/components/ui/tabs"
import { SpanHierarchy } from "./span-hierarchy"
import { Flamegraph } from "./flamegraph"
import { TraceFlowView } from "./flow-view"
diff --git a/apps/web/src/components/traces/traces-filter-sidebar.tsx b/apps/web/src/components/traces/traces-filter-sidebar.tsx
index b411b7f..8fd35c9 100644
--- a/apps/web/src/components/traces/traces-filter-sidebar.tsx
+++ b/apps/web/src/components/traces/traces-filter-sidebar.tsx
@@ -9,7 +9,7 @@ import {
} from "./filter-section"
import { DurationRangeFilter } from "./duration-range-filter"
import { Route } from "@/routes/traces"
-import { Separator } from "@/components/ui/separator"
+import { Separator } from "@maple/ui/components/ui/separator"
import { getTracesFacetsResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import {
FilterSidebarBody,
diff --git a/apps/web/src/components/traces/traces-table.tsx b/apps/web/src/components/traces/traces-table.tsx
index e0f2341..0ab0238 100644
--- a/apps/web/src/components/traces/traces-table.tsx
+++ b/apps/web/src/components/traces/traces-table.tsx
@@ -9,9 +9,9 @@ import {
TableHead,
TableHeader,
TableRow,
-} from "@/components/ui/table"
-import { Badge } from "@/components/ui/badge"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "@maple/ui/components/ui/table"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import { type Trace } from "@/api/tinybird/traces"
import { listTracesResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
import type { TracesSearchParams } from "@/routes/traces"
diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts
deleted file mode 100644
index bd0c391..0000000
--- a/apps/web/src/lib/utils.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { clsx, type ClassValue } from "clsx"
-import { twMerge } from "tailwind-merge"
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs))
-}
diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx
index ea7064d..471fd3f 100644
--- a/apps/web/src/routes/__root.tsx
+++ b/apps/web/src/routes/__root.tsx
@@ -1,7 +1,7 @@
import { useAuth } from "@clerk/clerk-react"
import { useCustomer } from "autumn-js/react"
import { Navigate, Outlet, createRootRouteWithContext, redirect, useRouterState } from "@tanstack/react-router"
-import { Toaster } from "@/components/ui/sonner"
+import { Toaster } from "@maple/ui/components/ui/sonner"
import { hasSelectedPlan } from "@/lib/billing/plan-gating"
import { isClerkAuthEnabled } from "@/lib/services/common/auth-mode"
import type { RouterAuthContext } from "@/router"
diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx
index dd05bed..e5b9f73 100644
--- a/apps/web/src/routes/index.tsx
+++ b/apps/web/src/routes/index.tsx
@@ -11,14 +11,14 @@ import {
SelectItem,
SelectTrigger,
SelectValue,
-} from "@/components/ui/select"
+} from "@maple/ui/components/ui/select"
import { useEffectiveTimeRange } from "@/hooks/use-effective-time-range"
import { ServiceUsageCards } from "@/components/dashboard/service-usage-cards"
import { MetricsGrid } from "@/components/dashboard/metrics-grid"
import type {
ChartLegendMode,
ChartTooltipMode,
-} from "@/components/charts/_shared/chart-types"
+} from "@maple/ui/components/charts/_shared/chart-types"
import {
getCustomChartTimeSeriesResultAtom,
getOverviewTimeSeriesResultAtom,
diff --git a/apps/web/src/routes/quick-start.tsx b/apps/web/src/routes/quick-start.tsx
index 9d3e99e..dc643de 100644
--- a/apps/web/src/routes/quick-start.tsx
+++ b/apps/web/src/routes/quick-start.tsx
@@ -9,9 +9,9 @@ import { DashboardLayout } from "@/components/layout/dashboard-layout"
import {
Card,
CardContent,
-} from "@/components/ui/card"
-import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"
-import { Button } from "@/components/ui/button"
+} from "@maple/ui/components/ui/card"
+import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@maple/ui/components/ui/input-group"
+import { Button } from "@maple/ui/components/ui/button"
import {
CheckIcon,
CopyIcon,
@@ -36,7 +36,7 @@ import { ingestUrl } from "@/lib/services/common/ingest-url"
import { MapleApiAtomClient } from "@/lib/services/common/atom-client"
import { useEffectiveTimeRange } from "@/hooks/use-effective-time-range"
import { getServiceOverviewResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
-import { cn } from "@/lib/utils"
+import { cn } from "@maple/ui/utils"
import { useCustomer } from "autumn-js/react"
import { hasSelectedPlan } from "@/lib/billing/plan-gating"
diff --git a/apps/web/src/routes/services/$serviceName.tsx b/apps/web/src/routes/services/$serviceName.tsx
index ca9233c..3e86bf9 100644
--- a/apps/web/src/routes/services/$serviceName.tsx
+++ b/apps/web/src/routes/services/$serviceName.tsx
@@ -9,7 +9,7 @@ import { MetricsGrid } from "@/components/dashboard/metrics-grid"
import type {
ChartLegendMode,
ChartTooltipMode,
-} from "@/components/charts/_shared/chart-types"
+} from "@maple/ui/components/charts/_shared/chart-types"
import {
getCustomChartServiceDetailResultAtom,
getServiceApdexTimeSeriesResultAtom,
diff --git a/apps/web/src/routes/settings.tsx b/apps/web/src/routes/settings.tsx
index e814b14..5c21fec 100644
--- a/apps/web/src/routes/settings.tsx
+++ b/apps/web/src/routes/settings.tsx
@@ -11,13 +11,13 @@ import {
CardDescription,
CardHeader,
CardTitle,
-} from "@/components/ui/card"
+} from "@maple/ui/components/ui/card"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
-} from "@/components/ui/input-group"
+} from "@maple/ui/components/ui/input-group"
import {
AlertDialog,
AlertDialogAction,
@@ -28,10 +28,10 @@ import {
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
-} from "@/components/ui/alert-dialog"
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
-import { Badge } from "@/components/ui/badge"
-import { Separator } from "@/components/ui/separator"
+} from "@maple/ui/components/ui/alert-dialog"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@maple/ui/components/ui/tabs"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Separator } from "@maple/ui/components/ui/separator"
import {
AlertWarningIcon,
CheckIcon,
diff --git a/apps/web/src/routes/sign-in.tsx b/apps/web/src/routes/sign-in.tsx
index 26bdb56..99237e5 100644
--- a/apps/web/src/routes/sign-in.tsx
+++ b/apps/web/src/routes/sign-in.tsx
@@ -2,8 +2,8 @@ import { SignIn } from "@clerk/clerk-react"
import { FormEvent, useState } from "react"
import { createFileRoute } from "@tanstack/react-router"
import { Schema } from "effect"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
+import { Button } from "@maple/ui/components/ui/button"
+import { Input } from "@maple/ui/components/ui/input"
import { apiBaseUrl } from "@/lib/services/common/api-base-url"
import { isClerkAuthEnabled } from "@/lib/services/common/auth-mode"
import { setSelfHostedSessionToken } from "@/lib/services/common/self-hosted-auth"
diff --git a/apps/web/src/routes/traces/$traceId.tsx b/apps/web/src/routes/traces/$traceId.tsx
index 0ab9868..e6b109a 100644
--- a/apps/web/src/routes/traces/$traceId.tsx
+++ b/apps/web/src/routes/traces/$traceId.tsx
@@ -6,13 +6,13 @@ import { toast } from "sonner"
import { DashboardLayout } from "@/components/layout/dashboard-layout"
import { TraceViewTabs } from "@/components/traces/trace-view-tabs"
import { SpanDetailPanel } from "@/components/traces/span-detail-panel"
-import { Badge } from "@/components/ui/badge"
-import { Skeleton } from "@/components/ui/skeleton"
+import { Badge } from "@maple/ui/components/ui/badge"
+import { Skeleton } from "@maple/ui/components/ui/skeleton"
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
-} from "@/components/ui/resizable"
+} from "@maple/ui/components/ui/resizable"
import { formatDuration } from "@/lib/format"
import { type Span, type SpanNode } from "@/api/tinybird/traces"
import { getSpanHierarchyResultAtom } from "@/lib/services/atoms/tinybird-query-atoms"
diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css
index 6a19dff..1a6e44a 100644
--- a/apps/web/src/styles.css
+++ b/apps/web/src/styles.css
@@ -2,6 +2,8 @@
@import "tw-animate-css";
@import "@fontsource-variable/geist-mono";
+@source "../../../packages/ui/src";
+
@custom-variant dark (&:is(.dark *));
:root {
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index 6b454fd..3c68551 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -16,9 +16,9 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
- "baseUrl": ".",
"paths": {
- "@/*": ["./src/*"]
+ "@/*": ["./src/*"],
+ "@maple/ui/*": ["../../packages/ui/src/*"]
},
"plugins": [
{
diff --git a/bun.lock b/bun.lock
index adfc508..989a3bf 100644
--- a/bun.lock
+++ b/bun.lock
@@ -50,6 +50,7 @@
"@clerk/clerk-react": "^5.60.0",
"@fontsource-variable/geist-mono": "^5.2.7",
"@maple/infra": "workspace:*",
+ "@maple/ui": "workspace:*",
"@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
@@ -60,6 +61,7 @@
"clsx": "^2.1.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
+ "recharts": "2.15.4",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
},
@@ -74,6 +76,7 @@
"@fontsource-variable/geist-mono": "^5.2.7",
"@maple/domain": "workspace:*",
"@maple/infra": "workspace:*",
+ "@maple/ui": "workspace:*",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "^0.9.5",
"@tanstack/react-router": "^1.159.5",
@@ -165,6 +168,35 @@
"typescript": "^5.9.3",
},
},
+ "packages/ui": {
+ "name": "@maple/ui",
+ "dependencies": {
+ "@base-ui/react": "^1.1.0",
+ "@xyflow/react": "^12.10.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "embla-carousel-react": "^8.6.0",
+ "input-otp": "^1.4.2",
+ "next-themes": "^0.4.6",
+ "react-day-picker": "^9.13.0",
+ "react-resizable-panels": "^4.6.2",
+ "recharts": "2.15.4",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.4.0",
+ "vaul": "^1.1.2",
+ },
+ "devDependencies": {
+ "@types/react": "^19.2.13",
+ "@types/react-dom": "^19.2.3",
+ "typescript": "^5.9.3",
+ },
+ "peerDependencies": {
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwindcss": "^4.0.0",
+ },
+ },
},
"packages": {
"@acemir/cssom": ["@acemir/cssom@0.9.31", "", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="],
@@ -587,6 +619,8 @@
"@maple/landing": ["@maple/landing@workspace:apps/landing"],
+ "@maple/ui": ["@maple/ui@workspace:packages/ui"],
+
"@maple/web": ["@maple/web@workspace:apps/web"],
"@mishieck/ink-titled-box": ["@mishieck/ink-titled-box@0.3.0", "", { "peerDependencies": { "ink": "^6.0.0", "react": "^19.1.0", "typescript": "^5" } }, "sha512-ugzVH9hixp3hwKfQ8On/qnsrdAxS3y9rTu/aGOFed4zVUvtZyGZNIR4rxAwXult8HKI4vJEh0OM8wib9NPrwUg=="],
diff --git a/packages/ui/package.json b/packages/ui/package.json
new file mode 100644
index 0000000..62949e2
--- /dev/null
+++ b/packages/ui/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@maple/ui",
+ "private": true,
+ "type": "module",
+ "exports": {
+ "./utils": "./src/lib/utils.ts",
+ "./colors": "./src/lib/colors.ts",
+ "./types": "./src/lib/types.ts",
+ "./format": "./src/lib/format.ts",
+ "./*": "./src/*"
+ },
+ "scripts": {
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@base-ui/react": "^1.1.0",
+ "@xyflow/react": "^12.10.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "embla-carousel-react": "^8.6.0",
+ "input-otp": "^1.4.2",
+ "next-themes": "^0.4.6",
+ "react-day-picker": "^9.13.0",
+ "react-resizable-panels": "^4.6.2",
+ "recharts": "2.15.4",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.4.0",
+ "vaul": "^1.1.2"
+ },
+ "peerDependencies": {
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwindcss": "^4.0.0"
+ },
+ "devDependencies": {
+ "@types/react": "^19.2.13",
+ "@types/react-dom": "^19.2.3",
+ "typescript": "^5.9.3"
+ }
+}
diff --git a/apps/web/src/components/charts/_shared/build-chart-config.ts b/packages/ui/src/components/charts/_shared/build-chart-config.ts
similarity index 92%
rename from apps/web/src/components/charts/_shared/build-chart-config.ts
rename to packages/ui/src/components/charts/_shared/build-chart-config.ts
index eace736..9a5df30 100644
--- a/apps/web/src/components/charts/_shared/build-chart-config.ts
+++ b/packages/ui/src/components/charts/_shared/build-chart-config.ts
@@ -1,4 +1,4 @@
-import type { ChartConfig } from "@/components/ui/chart"
+import type { ChartConfig } from "../../ui/chart"
const CHART_COLORS = [
"var(--chart-1)",
diff --git a/apps/web/src/components/charts/_shared/chart-types.ts b/packages/ui/src/components/charts/_shared/chart-types.ts
similarity index 100%
rename from apps/web/src/components/charts/_shared/chart-types.ts
rename to packages/ui/src/components/charts/_shared/chart-types.ts
diff --git a/apps/web/src/components/charts/_shared/sample-data.ts b/packages/ui/src/components/charts/_shared/sample-data.ts
similarity index 100%
rename from apps/web/src/components/charts/_shared/sample-data.ts
rename to packages/ui/src/components/charts/_shared/sample-data.ts
diff --git a/apps/web/src/components/charts/_shared/svg-filters.tsx b/packages/ui/src/components/charts/_shared/svg-filters.tsx
similarity index 100%
rename from apps/web/src/components/charts/_shared/svg-filters.tsx
rename to packages/ui/src/components/charts/_shared/svg-filters.tsx
diff --git a/apps/web/src/components/charts/_shared/svg-patterns.tsx b/packages/ui/src/components/charts/_shared/svg-patterns.tsx
similarity index 100%
rename from apps/web/src/components/charts/_shared/svg-patterns.tsx
rename to packages/ui/src/components/charts/_shared/svg-patterns.tsx
diff --git a/apps/web/src/components/charts/_shared/use-dynamic-dasharray.ts b/packages/ui/src/components/charts/_shared/use-dynamic-dasharray.ts
similarity index 100%
rename from apps/web/src/components/charts/_shared/use-dynamic-dasharray.ts
rename to packages/ui/src/components/charts/_shared/use-dynamic-dasharray.ts
diff --git a/apps/web/src/components/charts/area/apdex-area-chart.tsx b/packages/ui/src/components/charts/area/apdex-area-chart.tsx
similarity index 89%
rename from apps/web/src/components/charts/area/apdex-area-chart.tsx
rename to packages/ui/src/components/charts/area/apdex-area-chart.tsx
index 3c5abb2..a5eea04 100644
--- a/apps/web/src/components/charts/area/apdex-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/apdex-area-chart.tsx
@@ -1,9 +1,9 @@
import { useId, useMemo } from "react"
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { apdexTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { VerticalGradient } from "@/components/charts/_shared/svg-patterns"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { apdexTimeSeriesData } from "../_shared/sample-data"
+import { VerticalGradient } from "../_shared/svg-patterns"
import {
type ChartConfig,
ChartContainer,
@@ -11,8 +11,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { inferBucketSeconds, inferRangeMs, formatBucketLabel } from "@/lib/format"
+} from "../../ui/chart"
+import { inferBucketSeconds, inferRangeMs, formatBucketLabel } from "../../../lib/format"
const chartConfig = {
apdexScore: { label: "Apdex", color: "var(--chart-5)" },
diff --git a/apps/web/src/components/charts/area/bar-pattern-area-chart.tsx b/packages/ui/src/components/charts/area/bar-pattern-area-chart.tsx
similarity index 82%
rename from apps/web/src/components/charts/area/bar-pattern-area-chart.tsx
rename to packages/ui/src/components/charts/area/bar-pattern-area-chart.tsx
index 7392d11..33fb4e7 100644
--- a/apps/web/src/components/charts/area/bar-pattern-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/bar-pattern-area-chart.tsx
@@ -1,10 +1,10 @@
import { useId } from "react"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { areaTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { BarPattern } from "@/components/charts/_shared/svg-patterns"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { areaTimeSeriesData } from "../_shared/sample-data"
+import { BarPattern } from "../_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/area/dotted-pattern-area-chart.tsx b/packages/ui/src/components/charts/area/dotted-pattern-area-chart.tsx
similarity index 82%
rename from apps/web/src/components/charts/area/dotted-pattern-area-chart.tsx
rename to packages/ui/src/components/charts/area/dotted-pattern-area-chart.tsx
index 7c93cac..e65ee7a 100644
--- a/apps/web/src/components/charts/area/dotted-pattern-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/dotted-pattern-area-chart.tsx
@@ -1,10 +1,10 @@
import { useId } from "react"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { areaTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { DottedPattern } from "@/components/charts/_shared/svg-patterns"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { areaTimeSeriesData } from "../_shared/sample-data"
+import { DottedPattern } from "../_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/area/error-rate-area-chart.tsx b/packages/ui/src/components/charts/area/error-rate-area-chart.tsx
similarity index 89%
rename from apps/web/src/components/charts/area/error-rate-area-chart.tsx
rename to packages/ui/src/components/charts/area/error-rate-area-chart.tsx
index 303a8c6..dd86efb 100644
--- a/apps/web/src/components/charts/area/error-rate-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/error-rate-area-chart.tsx
@@ -1,9 +1,9 @@
import { useId, useMemo } from "react"
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { errorRateTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { VerticalGradient } from "@/components/charts/_shared/svg-patterns"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { errorRateTimeSeriesData } from "../_shared/sample-data"
+import { VerticalGradient } from "../_shared/svg-patterns"
import {
type ChartConfig,
ChartContainer,
@@ -11,8 +11,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { formatErrorRate, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "@/lib/format"
+} from "../../ui/chart"
+import { formatErrorRate, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "../../../lib/format"
const chartConfig = {
errorRate: { label: "Error Rate", color: "var(--color-destructive, #ef4444)" },
diff --git a/apps/web/src/components/charts/area/gradient-area-chart.tsx b/packages/ui/src/components/charts/area/gradient-area-chart.tsx
similarity index 83%
rename from apps/web/src/components/charts/area/gradient-area-chart.tsx
rename to packages/ui/src/components/charts/area/gradient-area-chart.tsx
index 8ebd7c1..4f64b0e 100644
--- a/apps/web/src/components/charts/area/gradient-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/gradient-area-chart.tsx
@@ -1,10 +1,10 @@
import { useId } from "react"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { areaTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { VerticalGradient } from "@/components/charts/_shared/svg-patterns"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { areaTimeSeriesData } from "../_shared/sample-data"
+import { VerticalGradient } from "../_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/area/gradient-rounded-area-chart.tsx b/packages/ui/src/components/charts/area/gradient-rounded-area-chart.tsx
similarity index 83%
rename from apps/web/src/components/charts/area/gradient-rounded-area-chart.tsx
rename to packages/ui/src/components/charts/area/gradient-rounded-area-chart.tsx
index 93ceff4..3071108 100644
--- a/apps/web/src/components/charts/area/gradient-rounded-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/gradient-rounded-area-chart.tsx
@@ -1,10 +1,10 @@
import { useId } from "react"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { areaTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { VerticalGradient } from "@/components/charts/_shared/svg-patterns"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { areaTimeSeriesData } from "../_shared/sample-data"
+import { VerticalGradient } from "../_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/area/query-builder-area-chart.tsx b/packages/ui/src/components/charts/area/query-builder-area-chart.tsx
similarity index 97%
rename from apps/web/src/components/charts/area/query-builder-area-chart.tsx
rename to packages/ui/src/components/charts/area/query-builder-area-chart.tsx
index 7896c1d..1f86869 100644
--- a/apps/web/src/components/charts/area/query-builder-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/query-builder-area-chart.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
+import type { BaseChartProps } from "../_shared/chart-types"
import {
type ChartConfig,
ChartContainer,
@@ -9,8 +9,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { formatNumber, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "@/lib/format"
+} from "../../ui/chart"
+import { formatNumber, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "../../../lib/format"
const fallbackData: Record[] = [
{ bucket: "2026-01-01T00:00:00Z", A: 12, B: 8 },
diff --git a/apps/web/src/components/charts/area/throughput-area-chart.tsx b/packages/ui/src/components/charts/area/throughput-area-chart.tsx
similarity index 90%
rename from apps/web/src/components/charts/area/throughput-area-chart.tsx
rename to packages/ui/src/components/charts/area/throughput-area-chart.tsx
index e1d3ab6..504dbba 100644
--- a/apps/web/src/components/charts/area/throughput-area-chart.tsx
+++ b/packages/ui/src/components/charts/area/throughput-area-chart.tsx
@@ -1,9 +1,9 @@
import { useMemo, useId } from "react"
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { throughputTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { VerticalGradient } from "@/components/charts/_shared/svg-patterns"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { throughputTimeSeriesData } from "../_shared/sample-data"
+import { VerticalGradient } from "../_shared/svg-patterns"
import {
type ChartConfig,
ChartContainer,
@@ -11,8 +11,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { inferBucketSeconds, inferRangeMs, formatBucketLabel, bucketIntervalLabel, formatThroughput } from "@/lib/format"
+} from "../../ui/chart"
+import { inferBucketSeconds, inferRangeMs, formatBucketLabel, bucketIntervalLabel, formatThroughput } from "../../../lib/format"
export function ThroughputAreaChart({ data, className, legend, tooltip }: BaseChartProps) {
const id = useId()
diff --git a/apps/web/src/components/charts/bar/default-bar-chart.tsx b/packages/ui/src/components/charts/bar/default-bar-chart.tsx
similarity index 74%
rename from apps/web/src/components/charts/bar/default-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/default-bar-chart.tsx
index 52301a8..936a47c 100644
--- a/apps/web/src/components/charts/bar/default-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/default-bar-chart.tsx
@@ -2,10 +2,10 @@
import { useId } from "react"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { defaultBarData } from "@/components/charts/_shared/sample-data"
-import { DottedPattern } from "@/components/charts/_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { defaultBarData } from "../_shared/sample-data"
+import { DottedPattern } from "../_shared/svg-patterns"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/default-multiple-bar-chart.tsx b/packages/ui/src/components/charts/bar/default-multiple-bar-chart.tsx
similarity index 81%
rename from apps/web/src/components/charts/bar/default-multiple-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/default-multiple-bar-chart.tsx
index c846688..6d6146c 100644
--- a/apps/web/src/components/charts/bar/default-multiple-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/default-multiple-bar-chart.tsx
@@ -2,10 +2,10 @@
import { useId } from "react"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { multiBarData } from "@/components/charts/_shared/sample-data"
-import { DottedPattern } from "@/components/charts/_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { multiBarData } from "../_shared/sample-data"
+import { DottedPattern } from "../_shared/svg-patterns"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/duotone-bar-chart.tsx b/packages/ui/src/components/charts/bar/duotone-bar-chart.tsx
similarity index 83%
rename from apps/web/src/components/charts/bar/duotone-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/duotone-bar-chart.tsx
index 6bdf441..cac4239 100644
--- a/apps/web/src/components/charts/bar/duotone-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/duotone-bar-chart.tsx
@@ -2,9 +2,9 @@
import { useId } from "react"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { defaultBarData } from "@/components/charts/_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { defaultBarData } from "../_shared/sample-data"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/glowing-bar-chart.tsx b/packages/ui/src/components/charts/bar/glowing-bar-chart.tsx
similarity index 80%
rename from apps/web/src/components/charts/bar/glowing-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/glowing-bar-chart.tsx
index 3dcfe2c..9c75ddb 100644
--- a/apps/web/src/components/charts/bar/glowing-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/glowing-bar-chart.tsx
@@ -2,10 +2,10 @@
import { useId } from "react"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { multiBarData } from "@/components/charts/_shared/sample-data"
-import { GlowFilter } from "@/components/charts/_shared/svg-filters"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { multiBarData } from "../_shared/sample-data"
+import { GlowFilter } from "../_shared/svg-filters"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/gradient-bar-chart.tsx b/packages/ui/src/components/charts/bar/gradient-bar-chart.tsx
similarity index 87%
rename from apps/web/src/components/charts/bar/gradient-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/gradient-bar-chart.tsx
index 98d9b47..c8aadcf 100644
--- a/apps/web/src/components/charts/bar/gradient-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/gradient-bar-chart.tsx
@@ -2,9 +2,9 @@
import { useId } from "react"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { defaultBarData } from "@/components/charts/_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { defaultBarData } from "../_shared/sample-data"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/hatched-bar-chart.tsx b/packages/ui/src/components/charts/bar/hatched-bar-chart.tsx
similarity index 74%
rename from apps/web/src/components/charts/bar/hatched-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/hatched-bar-chart.tsx
index ee7bf01..cd68648 100644
--- a/apps/web/src/components/charts/bar/hatched-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/hatched-bar-chart.tsx
@@ -2,10 +2,10 @@
import { useId } from "react"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { defaultBarData } from "@/components/charts/_shared/sample-data"
-import { HatchedPattern } from "@/components/charts/_shared/svg-patterns"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { defaultBarData } from "../_shared/sample-data"
+import { HatchedPattern } from "../_shared/svg-patterns"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/highlighted-bar-chart.tsx b/packages/ui/src/components/charts/bar/highlighted-bar-chart.tsx
similarity index 83%
rename from apps/web/src/components/charts/bar/highlighted-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/highlighted-bar-chart.tsx
index 341e7cd..61c6600 100644
--- a/apps/web/src/components/charts/bar/highlighted-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/highlighted-bar-chart.tsx
@@ -2,9 +2,9 @@
import { useState } from "react"
import { Bar, BarChart, CartesianGrid, Cell, XAxis } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { defaultBarData } from "@/components/charts/_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { defaultBarData } from "../_shared/sample-data"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/bar/query-builder-bar-chart.tsx b/packages/ui/src/components/charts/bar/query-builder-bar-chart.tsx
similarity index 96%
rename from apps/web/src/components/charts/bar/query-builder-bar-chart.tsx
rename to packages/ui/src/components/charts/bar/query-builder-bar-chart.tsx
index 8b00e85..59f0102 100644
--- a/apps/web/src/components/charts/bar/query-builder-bar-chart.tsx
+++ b/packages/ui/src/components/charts/bar/query-builder-bar-chart.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
+import type { BaseChartProps } from "../_shared/chart-types"
import {
type ChartConfig,
ChartContainer,
@@ -9,8 +9,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { formatNumber, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "@/lib/format"
+} from "../../ui/chart"
+import { formatNumber, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "../../../lib/format"
const fallbackData: Record[] = [
{ bucket: "2026-01-01T00:00:00Z", A: 12, B: 8 },
diff --git a/apps/web/src/components/charts/index.ts b/packages/ui/src/components/charts/index.ts
similarity index 100%
rename from apps/web/src/components/charts/index.ts
rename to packages/ui/src/components/charts/index.ts
diff --git a/apps/web/src/components/charts/line/dotted-line-chart.tsx b/packages/ui/src/components/charts/line/dotted-line-chart.tsx
similarity index 76%
rename from apps/web/src/components/charts/line/dotted-line-chart.tsx
rename to packages/ui/src/components/charts/line/dotted-line-chart.tsx
index 27cfff0..9407e46 100644
--- a/apps/web/src/components/charts/line/dotted-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/dotted-line-chart.tsx
@@ -1,8 +1,8 @@
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { lineTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { lineTimeSeriesData } from "../_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/dotted-multi-line-chart.tsx b/packages/ui/src/components/charts/line/dotted-multi-line-chart.tsx
similarity index 81%
rename from apps/web/src/components/charts/line/dotted-multi-line-chart.tsx
rename to packages/ui/src/components/charts/line/dotted-multi-line-chart.tsx
index b37ab5c..6cfc420 100644
--- a/apps/web/src/components/charts/line/dotted-multi-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/dotted-multi-line-chart.tsx
@@ -1,8 +1,8 @@
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { multiLineData } from "@/components/charts/_shared/sample-data"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { multiLineData } from "../_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/glowing-line-chart.tsx b/packages/ui/src/components/charts/line/glowing-line-chart.tsx
similarity index 75%
rename from apps/web/src/components/charts/line/glowing-line-chart.tsx
rename to packages/ui/src/components/charts/line/glowing-line-chart.tsx
index 3cffcb6..6ab192a 100644
--- a/apps/web/src/components/charts/line/glowing-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/glowing-line-chart.tsx
@@ -1,10 +1,10 @@
import { useId } from "react"
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { lineTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { GlowFilter } from "@/components/charts/_shared/svg-filters"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { lineTimeSeriesData } from "../_shared/sample-data"
+import { GlowFilter } from "../_shared/svg-filters"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/latency-line-chart.tsx b/packages/ui/src/components/charts/line/latency-line-chart.tsx
similarity index 92%
rename from apps/web/src/components/charts/line/latency-line-chart.tsx
rename to packages/ui/src/components/charts/line/latency-line-chart.tsx
index c6c24d2..55c4b98 100644
--- a/apps/web/src/components/charts/line/latency-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/latency-line-chart.tsx
@@ -1,8 +1,8 @@
import { useMemo } from "react"
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { latencyTimeSeriesData } from "@/components/charts/_shared/sample-data"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { latencyTimeSeriesData } from "../_shared/sample-data"
import {
type ChartConfig,
ChartContainer,
@@ -10,8 +10,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { formatLatency, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "@/lib/format"
+} from "../../ui/chart"
+import { formatLatency, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "../../../lib/format"
const chartConfig = {
p99LatencyMs: { label: "P99", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/number-dot-line-chart.tsx b/packages/ui/src/components/charts/line/number-dot-line-chart.tsx
similarity index 83%
rename from apps/web/src/components/charts/line/number-dot-line-chart.tsx
rename to packages/ui/src/components/charts/line/number-dot-line-chart.tsx
index 63e0025..30b3fcc 100644
--- a/apps/web/src/components/charts/line/number-dot-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/number-dot-line-chart.tsx
@@ -1,8 +1,8 @@
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { lineTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { lineTimeSeriesData } from "../_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/partial-line-chart.tsx b/packages/ui/src/components/charts/line/partial-line-chart.tsx
similarity index 81%
rename from apps/web/src/components/charts/line/partial-line-chart.tsx
rename to packages/ui/src/components/charts/line/partial-line-chart.tsx
index f039008..7a07da2 100644
--- a/apps/web/src/components/charts/line/partial-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/partial-line-chart.tsx
@@ -1,9 +1,9 @@
import { CartesianGrid, Customized, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { partialLineData } from "@/components/charts/_shared/sample-data"
-import { useDynamicDasharray } from "@/components/charts/_shared/use-dynamic-dasharray"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { partialLineData } from "../_shared/sample-data"
+import { useDynamicDasharray } from "../_shared/use-dynamic-dasharray"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/pinging-dot-chart.tsx b/packages/ui/src/components/charts/line/pinging-dot-chart.tsx
similarity index 87%
rename from apps/web/src/components/charts/line/pinging-dot-chart.tsx
rename to packages/ui/src/components/charts/line/pinging-dot-chart.tsx
index 6c82267..b3cd2c8 100644
--- a/apps/web/src/components/charts/line/pinging-dot-chart.tsx
+++ b/packages/ui/src/components/charts/line/pinging-dot-chart.tsx
@@ -1,8 +1,8 @@
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { lineTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { lineTimeSeriesData } from "../_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/line/query-builder-line-chart.tsx b/packages/ui/src/components/charts/line/query-builder-line-chart.tsx
similarity index 97%
rename from apps/web/src/components/charts/line/query-builder-line-chart.tsx
rename to packages/ui/src/components/charts/line/query-builder-line-chart.tsx
index 78eff76..6d79508 100644
--- a/apps/web/src/components/charts/line/query-builder-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/query-builder-line-chart.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
+import type { BaseChartProps } from "../_shared/chart-types"
import {
type ChartConfig,
ChartContainer,
@@ -9,8 +9,8 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from "@/components/ui/chart"
-import { formatNumber, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "@/lib/format"
+} from "../../ui/chart"
+import { formatNumber, inferBucketSeconds, inferRangeMs, formatBucketLabel } from "../../../lib/format"
const fallbackData: Record[] = [
{ bucket: "2026-01-01T00:00:00Z", A: 12, B: 8 },
diff --git a/apps/web/src/components/charts/line/rainbow-glow-gradient-line-chart.tsx b/packages/ui/src/components/charts/line/rainbow-glow-gradient-line-chart.tsx
similarity index 76%
rename from apps/web/src/components/charts/line/rainbow-glow-gradient-line-chart.tsx
rename to packages/ui/src/components/charts/line/rainbow-glow-gradient-line-chart.tsx
index 80c8bce..d25c005 100644
--- a/apps/web/src/components/charts/line/rainbow-glow-gradient-line-chart.tsx
+++ b/packages/ui/src/components/charts/line/rainbow-glow-gradient-line-chart.tsx
@@ -1,10 +1,10 @@
import { useId } from "react"
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { lineTimeSeriesData } from "@/components/charts/_shared/sample-data"
-import { GlowFilter, RainbowGradient } from "@/components/charts/_shared/svg-filters"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { lineTimeSeriesData } from "../_shared/sample-data"
+import { GlowFilter, RainbowGradient } from "../_shared/svg-filters"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
const chartConfig = {
value: { label: "Value", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/pie/default-radial-chart.tsx b/packages/ui/src/components/charts/pie/default-radial-chart.tsx
similarity index 69%
rename from apps/web/src/components/charts/pie/default-radial-chart.tsx
rename to packages/ui/src/components/charts/pie/default-radial-chart.tsx
index cf48dda..e668a8d 100644
--- a/apps/web/src/components/charts/pie/default-radial-chart.tsx
+++ b/packages/ui/src/components/charts/pie/default-radial-chart.tsx
@@ -2,10 +2,10 @@
import { useMemo } from "react"
import { RadialBar, RadialBarChart } from "recharts"
-import { ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { buildChartConfig } from "@/components/charts/_shared/build-chart-config"
-import { radialData } from "@/components/charts/_shared/sample-data"
+import { ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { buildChartConfig } from "../_shared/build-chart-config"
+import { radialData } from "../_shared/sample-data"
export function DefaultRadialChart({ data = radialData, className }: BaseChartProps) {
const { config, data: coloredData } = useMemo(() => buildChartConfig(data), [data])
diff --git a/apps/web/src/components/charts/pie/glowing-radial-chart.tsx b/packages/ui/src/components/charts/pie/glowing-radial-chart.tsx
similarity index 79%
rename from apps/web/src/components/charts/pie/glowing-radial-chart.tsx
rename to packages/ui/src/components/charts/pie/glowing-radial-chart.tsx
index 85ad3e2..76bc127 100644
--- a/apps/web/src/components/charts/pie/glowing-radial-chart.tsx
+++ b/packages/ui/src/components/charts/pie/glowing-radial-chart.tsx
@@ -2,11 +2,11 @@
import { useId, useMemo, useState } from "react"
import { RadialBar, RadialBarChart } from "recharts"
-import { ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { buildChartConfig } from "@/components/charts/_shared/build-chart-config"
-import { radialData } from "@/components/charts/_shared/sample-data"
-import { GlowFilter } from "@/components/charts/_shared/svg-filters"
+import { ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { buildChartConfig } from "../_shared/build-chart-config"
+import { radialData } from "../_shared/sample-data"
+import { GlowFilter } from "../_shared/svg-filters"
export function GlowingRadialChart({ data = radialData, className }: BaseChartProps) {
const id = useId()
diff --git a/apps/web/src/components/charts/pie/increase-size-pie-chart.tsx b/packages/ui/src/components/charts/pie/increase-size-pie-chart.tsx
similarity index 77%
rename from apps/web/src/components/charts/pie/increase-size-pie-chart.tsx
rename to packages/ui/src/components/charts/pie/increase-size-pie-chart.tsx
index 4789fe4..bf2d022 100644
--- a/apps/web/src/components/charts/pie/increase-size-pie-chart.tsx
+++ b/packages/ui/src/components/charts/pie/increase-size-pie-chart.tsx
@@ -2,10 +2,10 @@
import { useMemo } from "react"
import { Cell, Pie, PieChart } from "recharts"
-import { ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { buildChartConfig } from "@/components/charts/_shared/build-chart-config"
-import { pieData } from "@/components/charts/_shared/sample-data"
+import { ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { buildChartConfig } from "../_shared/build-chart-config"
+import { pieData } from "../_shared/sample-data"
export function IncreaseSizePieChart({ data = pieData, className }: BaseChartProps) {
const { config, data: coloredData } = useMemo(() => buildChartConfig(data), [data])
diff --git a/apps/web/src/components/charts/pie/rounded-pie-chart.tsx b/packages/ui/src/components/charts/pie/rounded-pie-chart.tsx
similarity index 71%
rename from apps/web/src/components/charts/pie/rounded-pie-chart.tsx
rename to packages/ui/src/components/charts/pie/rounded-pie-chart.tsx
index 84b8aa6..aad852b 100644
--- a/apps/web/src/components/charts/pie/rounded-pie-chart.tsx
+++ b/packages/ui/src/components/charts/pie/rounded-pie-chart.tsx
@@ -2,10 +2,10 @@
import { useMemo } from "react"
import { LabelList, Pie, PieChart } from "recharts"
-import { ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { buildChartConfig } from "@/components/charts/_shared/build-chart-config"
-import { pieData } from "@/components/charts/_shared/sample-data"
+import { ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { buildChartConfig } from "../_shared/build-chart-config"
+import { pieData } from "../_shared/sample-data"
export function RoundedPieChart({ data = pieData, className }: BaseChartProps) {
const { config, data: coloredData } = useMemo(() => buildChartConfig(data), [data])
diff --git a/apps/web/src/components/charts/radar/glowing-multiple-stroke-radar-chart.tsx b/packages/ui/src/components/charts/radar/glowing-multiple-stroke-radar-chart.tsx
similarity index 80%
rename from apps/web/src/components/charts/radar/glowing-multiple-stroke-radar-chart.tsx
rename to packages/ui/src/components/charts/radar/glowing-multiple-stroke-radar-chart.tsx
index 8fe393c..5cfa32a 100644
--- a/apps/web/src/components/charts/radar/glowing-multiple-stroke-radar-chart.tsx
+++ b/packages/ui/src/components/charts/radar/glowing-multiple-stroke-radar-chart.tsx
@@ -2,10 +2,10 @@
import { useId } from "react"
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { radarData } from "@/components/charts/_shared/sample-data"
-import { GlowFilter } from "@/components/charts/_shared/svg-filters"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { radarData } from "../_shared/sample-data"
+import { GlowFilter } from "../_shared/svg-filters"
const chartConfig = {
a: { label: "Student A", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/radar/glowing-stroke-radar-chart.tsx b/packages/ui/src/components/charts/radar/glowing-stroke-radar-chart.tsx
similarity index 75%
rename from apps/web/src/components/charts/radar/glowing-stroke-radar-chart.tsx
rename to packages/ui/src/components/charts/radar/glowing-stroke-radar-chart.tsx
index ae1f7b5..f05f2d5 100644
--- a/apps/web/src/components/charts/radar/glowing-stroke-radar-chart.tsx
+++ b/packages/ui/src/components/charts/radar/glowing-stroke-radar-chart.tsx
@@ -2,10 +2,10 @@
import { useId } from "react"
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { radarData } from "@/components/charts/_shared/sample-data"
-import { GlowFilter } from "@/components/charts/_shared/svg-filters"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { radarData } from "../_shared/sample-data"
+import { GlowFilter } from "../_shared/svg-filters"
const chartConfig = {
a: { label: "Student A", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/radar/stroke-multiple-radar-chart.tsx b/packages/ui/src/components/charts/radar/stroke-multiple-radar-chart.tsx
similarity index 81%
rename from apps/web/src/components/charts/radar/stroke-multiple-radar-chart.tsx
rename to packages/ui/src/components/charts/radar/stroke-multiple-radar-chart.tsx
index c14e013..363b630 100644
--- a/apps/web/src/components/charts/radar/stroke-multiple-radar-chart.tsx
+++ b/packages/ui/src/components/charts/radar/stroke-multiple-radar-chart.tsx
@@ -1,9 +1,9 @@
"use client"
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { radarData } from "@/components/charts/_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { radarData } from "../_shared/sample-data"
const chartConfig = {
a: { label: "Student A", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/radar/stroke-radar-chart.tsx b/packages/ui/src/components/charts/radar/stroke-radar-chart.tsx
similarity index 76%
rename from apps/web/src/components/charts/radar/stroke-radar-chart.tsx
rename to packages/ui/src/components/charts/radar/stroke-radar-chart.tsx
index 1da0934..b7038bb 100644
--- a/apps/web/src/components/charts/radar/stroke-radar-chart.tsx
+++ b/packages/ui/src/components/charts/radar/stroke-radar-chart.tsx
@@ -1,9 +1,9 @@
"use client"
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
-import { type ChartConfig, ChartContainer } from "@/components/ui/chart"
-import type { BaseChartProps } from "@/components/charts/_shared/chart-types"
-import { radarData } from "@/components/charts/_shared/sample-data"
+import { type ChartConfig, ChartContainer } from "../../ui/chart"
+import type { BaseChartProps } from "../_shared/chart-types"
+import { radarData } from "../_shared/sample-data"
const chartConfig = {
a: { label: "Student A", color: "var(--chart-1)" },
diff --git a/apps/web/src/components/charts/registry.ts b/packages/ui/src/components/charts/registry.ts
similarity index 100%
rename from apps/web/src/components/charts/registry.ts
rename to packages/ui/src/components/charts/registry.ts
diff --git a/packages/ui/src/components/icons/alert-warning.tsx b/packages/ui/src/components/icons/alert-warning.tsx
new file mode 100644
index 0000000..724b809
--- /dev/null
+++ b/packages/ui/src/components/icons/alert-warning.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function AlertWarningIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { AlertWarningIcon }
diff --git a/packages/ui/src/components/icons/arrow-down.tsx b/packages/ui/src/components/icons/arrow-down.tsx
new file mode 100644
index 0000000..f4e3e05
--- /dev/null
+++ b/packages/ui/src/components/icons/arrow-down.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function ArrowDownIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { ArrowDownIcon }
diff --git a/packages/ui/src/components/icons/arrow-left.tsx b/packages/ui/src/components/icons/arrow-left.tsx
new file mode 100644
index 0000000..4a580c7
--- /dev/null
+++ b/packages/ui/src/components/icons/arrow-left.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function ArrowLeftIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { ArrowLeftIcon }
diff --git a/packages/ui/src/components/icons/arrow-right.tsx b/packages/ui/src/components/icons/arrow-right.tsx
new file mode 100644
index 0000000..54a2421
--- /dev/null
+++ b/packages/ui/src/components/icons/arrow-right.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function ArrowRightIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { ArrowRightIcon }
diff --git a/packages/ui/src/components/icons/check.tsx b/packages/ui/src/components/icons/check.tsx
new file mode 100644
index 0000000..76ba375
--- /dev/null
+++ b/packages/ui/src/components/icons/check.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function CheckIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { CheckIcon }
diff --git a/packages/ui/src/components/icons/chevron-down.tsx b/packages/ui/src/components/icons/chevron-down.tsx
new file mode 100644
index 0000000..cac0ec6
--- /dev/null
+++ b/packages/ui/src/components/icons/chevron-down.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function ChevronDownIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { ChevronDownIcon }
diff --git a/packages/ui/src/components/icons/chevron-expand-y.tsx b/packages/ui/src/components/icons/chevron-expand-y.tsx
new file mode 100644
index 0000000..ef41169
--- /dev/null
+++ b/packages/ui/src/components/icons/chevron-expand-y.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function ChevronExpandYIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { ChevronExpandYIcon }
diff --git a/packages/ui/src/components/icons/chevron-left.tsx b/packages/ui/src/components/icons/chevron-left.tsx
new file mode 100644
index 0000000..4baf07c
--- /dev/null
+++ b/packages/ui/src/components/icons/chevron-left.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function ChevronLeftIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { ChevronLeftIcon }
diff --git a/packages/ui/src/components/icons/chevron-right.tsx b/packages/ui/src/components/icons/chevron-right.tsx
new file mode 100644
index 0000000..0256431
--- /dev/null
+++ b/packages/ui/src/components/icons/chevron-right.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function ChevronRightIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { ChevronRightIcon }
diff --git a/packages/ui/src/components/icons/chevron-up.tsx b/packages/ui/src/components/icons/chevron-up.tsx
new file mode 100644
index 0000000..5e99452
--- /dev/null
+++ b/packages/ui/src/components/icons/chevron-up.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function ChevronUpIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { ChevronUpIcon }
diff --git a/packages/ui/src/components/icons/circle-check.tsx b/packages/ui/src/components/icons/circle-check.tsx
new file mode 100644
index 0000000..6863ec2
--- /dev/null
+++ b/packages/ui/src/components/icons/circle-check.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function CircleCheckIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { CircleCheckIcon }
diff --git a/packages/ui/src/components/icons/circle-info.tsx b/packages/ui/src/components/icons/circle-info.tsx
new file mode 100644
index 0000000..79effe5
--- /dev/null
+++ b/packages/ui/src/components/icons/circle-info.tsx
@@ -0,0 +1,13 @@
+import type { IconProps } from "./icon"
+
+function CircleInfoIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+
+ )
+}
+export { CircleInfoIcon }
diff --git a/packages/ui/src/components/icons/circle-xmark.tsx b/packages/ui/src/components/icons/circle-xmark.tsx
new file mode 100644
index 0000000..126c392
--- /dev/null
+++ b/packages/ui/src/components/icons/circle-xmark.tsx
@@ -0,0 +1,13 @@
+import type { IconProps } from "./icon"
+
+function CircleXmarkIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+
+ )
+}
+export { CircleXmarkIcon }
diff --git a/packages/ui/src/components/icons/code.tsx b/packages/ui/src/components/icons/code.tsx
new file mode 100644
index 0000000..e1dd563
--- /dev/null
+++ b/packages/ui/src/components/icons/code.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function CodeIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { CodeIcon }
diff --git a/packages/ui/src/components/icons/dots.tsx b/packages/ui/src/components/icons/dots.tsx
new file mode 100644
index 0000000..1f31c50
--- /dev/null
+++ b/packages/ui/src/components/icons/dots.tsx
@@ -0,0 +1,13 @@
+import type { IconProps } from "./icon"
+
+function DotsIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+
+ )
+}
+export { DotsIcon }
diff --git a/packages/ui/src/components/icons/eye.tsx b/packages/ui/src/components/icons/eye.tsx
new file mode 100644
index 0000000..ebbe36e
--- /dev/null
+++ b/packages/ui/src/components/icons/eye.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function EyeIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { EyeIcon }
diff --git a/packages/ui/src/components/icons/icon.tsx b/packages/ui/src/components/icons/icon.tsx
new file mode 100644
index 0000000..87d3788
--- /dev/null
+++ b/packages/ui/src/components/icons/icon.tsx
@@ -0,0 +1,7 @@
+import type { SVGProps, ComponentType } from "react"
+
+export interface IconProps extends SVGProps {
+ size?: number | string
+}
+
+export type IconComponent = ComponentType
diff --git a/packages/ui/src/components/icons/index.ts b/packages/ui/src/components/icons/index.ts
new file mode 100644
index 0000000..1f32292
--- /dev/null
+++ b/packages/ui/src/components/icons/index.ts
@@ -0,0 +1,26 @@
+export type { IconProps, IconComponent } from "./icon"
+
+export { AlertWarningIcon } from "./alert-warning"
+export { ArrowDownIcon } from "./arrow-down"
+export { ArrowLeftIcon } from "./arrow-left"
+export { ArrowRightIcon } from "./arrow-right"
+export { CheckIcon } from "./check"
+export { ChevronDownIcon } from "./chevron-down"
+export { ChevronExpandYIcon } from "./chevron-expand-y"
+export { ChevronLeftIcon } from "./chevron-left"
+export { ChevronRightIcon } from "./chevron-right"
+export { ChevronUpIcon } from "./chevron-up"
+export { CircleCheckIcon } from "./circle-check"
+export { CircleInfoIcon } from "./circle-info"
+export { CircleXmarkIcon } from "./circle-xmark"
+export { CodeIcon } from "./code"
+export { DotsIcon } from "./dots"
+export { EyeIcon } from "./eye"
+export { LoaderIcon } from "./loader"
+export { MagnifierIcon } from "./magnifier"
+export { MinusIcon } from "./minus"
+export { NetworkNodesIcon } from "./network-nodes"
+export { PulseIcon } from "./pulse"
+export { RadioCheckedIcon } from "./radio-checked"
+export { SidebarLeftIcon } from "./sidebar-left"
+export { XmarkIcon } from "./xmark"
diff --git a/packages/ui/src/components/icons/loader.tsx b/packages/ui/src/components/icons/loader.tsx
new file mode 100644
index 0000000..0ef8f9f
--- /dev/null
+++ b/packages/ui/src/components/icons/loader.tsx
@@ -0,0 +1,18 @@
+import type { IconProps } from "./icon"
+
+function LoaderIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+export { LoaderIcon }
diff --git a/packages/ui/src/components/icons/magnifier.tsx b/packages/ui/src/components/icons/magnifier.tsx
new file mode 100644
index 0000000..6530791
--- /dev/null
+++ b/packages/ui/src/components/icons/magnifier.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function MagnifierIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { MagnifierIcon }
diff --git a/packages/ui/src/components/icons/minus.tsx b/packages/ui/src/components/icons/minus.tsx
new file mode 100644
index 0000000..090c552
--- /dev/null
+++ b/packages/ui/src/components/icons/minus.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function MinusIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { MinusIcon }
diff --git a/packages/ui/src/components/icons/network-nodes.tsx b/packages/ui/src/components/icons/network-nodes.tsx
new file mode 100644
index 0000000..104270a
--- /dev/null
+++ b/packages/ui/src/components/icons/network-nodes.tsx
@@ -0,0 +1,19 @@
+import type { IconProps } from "./icon"
+
+function NetworkNodesIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+export { NetworkNodesIcon }
diff --git a/packages/ui/src/components/icons/pulse.tsx b/packages/ui/src/components/icons/pulse.tsx
new file mode 100644
index 0000000..8ef2cc4
--- /dev/null
+++ b/packages/ui/src/components/icons/pulse.tsx
@@ -0,0 +1,11 @@
+import type { IconProps } from "./icon"
+
+function PulseIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+ )
+}
+export { PulseIcon }
diff --git a/packages/ui/src/components/icons/radio-checked.tsx b/packages/ui/src/components/icons/radio-checked.tsx
new file mode 100644
index 0000000..63b3a77
--- /dev/null
+++ b/packages/ui/src/components/icons/radio-checked.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function RadioCheckedIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { RadioCheckedIcon }
diff --git a/packages/ui/src/components/icons/sidebar-left.tsx b/packages/ui/src/components/icons/sidebar-left.tsx
new file mode 100644
index 0000000..014deb8
--- /dev/null
+++ b/packages/ui/src/components/icons/sidebar-left.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function SidebarLeftIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { SidebarLeftIcon }
diff --git a/packages/ui/src/components/icons/xmark.tsx b/packages/ui/src/components/icons/xmark.tsx
new file mode 100644
index 0000000..43f6eb6
--- /dev/null
+++ b/packages/ui/src/components/icons/xmark.tsx
@@ -0,0 +1,12 @@
+import type { IconProps } from "./icon"
+
+function XmarkIcon({ size = 24, className, ...props }: IconProps) {
+ return (
+
+
+
+
+ )
+}
+export { XmarkIcon }
diff --git a/apps/landing/src/components/traces/flamegraph-minimap.tsx b/packages/ui/src/components/traces/flamegraph-minimap.tsx
similarity index 100%
rename from apps/landing/src/components/traces/flamegraph-minimap.tsx
rename to packages/ui/src/components/traces/flamegraph-minimap.tsx
diff --git a/apps/landing/src/components/traces/flamegraph-tooltip.tsx b/packages/ui/src/components/traces/flamegraph-tooltip.tsx
similarity index 100%
rename from apps/landing/src/components/traces/flamegraph-tooltip.tsx
rename to packages/ui/src/components/traces/flamegraph-tooltip.tsx
diff --git a/apps/landing/src/components/traces/flamegraph.tsx b/packages/ui/src/components/traces/flamegraph.tsx
similarity index 100%
rename from apps/landing/src/components/traces/flamegraph.tsx
rename to packages/ui/src/components/traces/flamegraph.tsx
diff --git a/apps/landing/src/components/traces/flow-node.tsx b/packages/ui/src/components/traces/flow-node.tsx
similarity index 100%
rename from apps/landing/src/components/traces/flow-node.tsx
rename to packages/ui/src/components/traces/flow-node.tsx
diff --git a/apps/landing/src/components/traces/flow-utils.ts b/packages/ui/src/components/traces/flow-utils.ts
similarity index 100%
rename from apps/landing/src/components/traces/flow-utils.ts
rename to packages/ui/src/components/traces/flow-utils.ts
diff --git a/apps/landing/src/components/traces/flow-view.tsx b/packages/ui/src/components/traces/flow-view.tsx
similarity index 100%
rename from apps/landing/src/components/traces/flow-view.tsx
rename to packages/ui/src/components/traces/flow-view.tsx
diff --git a/apps/web/src/components/ui/accordion.tsx b/packages/ui/src/components/ui/accordion.tsx
similarity index 96%
rename from apps/web/src/components/ui/accordion.tsx
rename to packages/ui/src/components/ui/accordion.tsx
index ec21e0c..ec45c0d 100644
--- a/apps/web/src/components/ui/accordion.tsx
+++ b/packages/ui/src/components/ui/accordion.tsx
@@ -1,7 +1,7 @@
import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
-import { cn } from "@/lib/utils"
-import { ChevronDownIcon, ChevronUpIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronDownIcon, ChevronUpIcon } from "../icons"
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
return (
diff --git a/apps/web/src/components/ui/alert-dialog.tsx b/packages/ui/src/components/ui/alert-dialog.tsx
similarity index 98%
rename from apps/web/src/components/ui/alert-dialog.tsx
rename to packages/ui/src/components/ui/alert-dialog.tsx
index 35028c5..d35cab8 100644
--- a/apps/web/src/components/ui/alert-dialog.tsx
+++ b/packages/ui/src/components/ui/alert-dialog.tsx
@@ -1,8 +1,8 @@
import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/alert.tsx b/packages/ui/src/components/ui/alert.tsx
similarity index 98%
rename from apps/web/src/components/ui/alert.tsx
rename to packages/ui/src/components/ui/alert.tsx
index 08916e5..f0fd300 100644
--- a/apps/web/src/components/ui/alert.tsx
+++ b/packages/ui/src/components/ui/alert.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
const alertVariants = cva("grid gap-0.5 rounded-none border px-2.5 py-2 text-left text-xs has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert", {
variants: {
diff --git a/apps/web/src/components/ui/aspect-ratio.tsx b/packages/ui/src/components/ui/aspect-ratio.tsx
similarity index 90%
rename from apps/web/src/components/ui/aspect-ratio.tsx
rename to packages/ui/src/components/ui/aspect-ratio.tsx
index 4c0ebaa..400573e 100644
--- a/apps/web/src/components/ui/aspect-ratio.tsx
+++ b/packages/ui/src/components/ui/aspect-ratio.tsx
@@ -1,4 +1,4 @@
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function AspectRatio({
ratio,
diff --git a/apps/web/src/components/ui/avatar.tsx b/packages/ui/src/components/ui/avatar.tsx
similarity index 98%
rename from apps/web/src/components/ui/avatar.tsx
rename to packages/ui/src/components/ui/avatar.tsx
index 6c744e2..b1b78ba 100644
--- a/apps/web/src/components/ui/avatar.tsx
+++ b/packages/ui/src/components/ui/avatar.tsx
@@ -3,7 +3,7 @@
import * as React from "react"
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Avatar({
className,
diff --git a/apps/web/src/components/ui/badge.tsx b/packages/ui/src/components/ui/badge.tsx
similarity index 98%
rename from apps/web/src/components/ui/badge.tsx
rename to packages/ui/src/components/ui/badge.tsx
index 6d23feb..b1a243b 100644
--- a/apps/web/src/components/ui/badge.tsx
+++ b/packages/ui/src/components/ui/badge.tsx
@@ -2,7 +2,7 @@ import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
const badgeVariants = cva(
"h-5 gap-1 rounded-none border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge",
diff --git a/apps/web/src/components/ui/breadcrumb.tsx b/packages/ui/src/components/ui/breadcrumb.tsx
similarity index 96%
rename from apps/web/src/components/ui/breadcrumb.tsx
rename to packages/ui/src/components/ui/breadcrumb.tsx
index 935f09c..ee8e24d 100644
--- a/apps/web/src/components/ui/breadcrumb.tsx
+++ b/packages/ui/src/components/ui/breadcrumb.tsx
@@ -2,8 +2,8 @@ import * as React from "react"
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
-import { cn } from "@/lib/utils"
-import { ChevronRightIcon, DotsIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronRightIcon, DotsIcon } from "../icons"
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
return (
diff --git a/apps/web/src/components/ui/button-group.tsx b/packages/ui/src/components/ui/button-group.tsx
similarity index 96%
rename from apps/web/src/components/ui/button-group.tsx
rename to packages/ui/src/components/ui/button-group.tsx
index 587c0af..70b74a4 100644
--- a/apps/web/src/components/ui/button-group.tsx
+++ b/packages/ui/src/components/ui/button-group.tsx
@@ -2,8 +2,8 @@ import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { Separator } from "@/components/ui/separator"
+import { cn } from "../../lib/utils"
+import { Separator } from "./separator"
const buttonGroupVariants = cva(
"rounded-none has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-none flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
diff --git a/apps/web/src/components/ui/button.tsx b/packages/ui/src/components/ui/button.tsx
similarity index 98%
rename from apps/web/src/components/ui/button.tsx
rename to packages/ui/src/components/ui/button.tsx
index c9ed23b..1c3d928 100644
--- a/apps/web/src/components/ui/button.tsx
+++ b/packages/ui/src/components/ui/button.tsx
@@ -1,7 +1,7 @@
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
const buttonVariants = cva(
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
diff --git a/apps/web/src/components/ui/calendar.tsx b/packages/ui/src/components/ui/calendar.tsx
similarity index 97%
rename from apps/web/src/components/ui/calendar.tsx
rename to packages/ui/src/components/ui/calendar.tsx
index 51fcb56..f055d26 100644
--- a/apps/web/src/components/ui/calendar.tsx
+++ b/packages/ui/src/components/ui/calendar.tsx
@@ -8,9 +8,9 @@ import {
type Locale,
} from "react-day-picker"
-import { cn } from "@/lib/utils"
-import { Button, buttonVariants } from "@/components/ui/button"
-import { ArrowLeftIcon, ArrowRightIcon, ArrowDownIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { Button, buttonVariants } from "./button"
+import { ArrowLeftIcon, ArrowRightIcon, ArrowDownIcon } from "../icons"
function Calendar({
className,
diff --git a/apps/web/src/components/ui/card.tsx b/packages/ui/src/components/ui/card.tsx
similarity index 98%
rename from apps/web/src/components/ui/card.tsx
rename to packages/ui/src/components/ui/card.tsx
index d63cd65..4d4f51c 100644
--- a/apps/web/src/components/ui/card.tsx
+++ b/packages/ui/src/components/ui/card.tsx
@@ -1,6 +1,6 @@
import * as React from "react"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Card({
className,
diff --git a/apps/web/src/components/ui/carousel.tsx b/packages/ui/src/components/ui/carousel.tsx
similarity index 97%
rename from apps/web/src/components/ui/carousel.tsx
rename to packages/ui/src/components/ui/carousel.tsx
index 2838e48..1028379 100644
--- a/apps/web/src/components/ui/carousel.tsx
+++ b/packages/ui/src/components/ui/carousel.tsx
@@ -3,9 +3,9 @@ import useEmblaCarousel, {
type UseEmblaCarouselType,
} from "embla-carousel-react"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { ChevronLeftIcon, ChevronRightIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
+import { ChevronLeftIcon, ChevronRightIcon } from "../icons"
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters
diff --git a/apps/web/src/components/ui/chart.tsx b/packages/ui/src/components/ui/chart.tsx
similarity index 99%
rename from apps/web/src/components/ui/chart.tsx
rename to packages/ui/src/components/ui/chart.tsx
index 19d8ada..3ceb097 100644
--- a/apps/web/src/components/ui/chart.tsx
+++ b/packages/ui/src/components/ui/chart.tsx
@@ -3,7 +3,7 @@
import * as React from "react"
import * as RechartsPrimitive from "recharts"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const
diff --git a/apps/web/src/components/ui/checkbox.tsx b/packages/ui/src/components/ui/checkbox.tsx
similarity index 94%
rename from apps/web/src/components/ui/checkbox.tsx
rename to packages/ui/src/components/ui/checkbox.tsx
index a3b183c..22226f3 100644
--- a/apps/web/src/components/ui/checkbox.tsx
+++ b/packages/ui/src/components/ui/checkbox.tsx
@@ -2,8 +2,8 @@
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
-import { cn } from "@/lib/utils"
-import { CheckIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { CheckIcon } from "../icons"
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
return (
diff --git a/apps/web/src/components/ui/collapsible.tsx b/packages/ui/src/components/ui/collapsible.tsx
similarity index 100%
rename from apps/web/src/components/ui/collapsible.tsx
rename to packages/ui/src/components/ui/collapsible.tsx
diff --git a/apps/web/src/components/ui/combobox.tsx b/packages/ui/src/components/ui/combobox.tsx
similarity index 97%
rename from apps/web/src/components/ui/combobox.tsx
rename to packages/ui/src/components/ui/combobox.tsx
index 96f7463..e2dc1b6 100644
--- a/apps/web/src/components/ui/combobox.tsx
+++ b/packages/ui/src/components/ui/combobox.tsx
@@ -3,15 +3,15 @@
import * as React from "react"
import { Combobox as ComboboxPrimitive } from "@base-ui/react"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
-} from "@/components/ui/input-group"
-import { ChevronDownIcon, XmarkIcon, CheckIcon } from "@/components/icons"
+} from "./input-group"
+import { ChevronDownIcon, XmarkIcon, CheckIcon } from "../icons"
const Combobox = ComboboxPrimitive.Root
diff --git a/apps/web/src/components/ui/command.tsx b/packages/ui/src/components/ui/command.tsx
similarity index 96%
rename from apps/web/src/components/ui/command.tsx
rename to packages/ui/src/components/ui/command.tsx
index 278bc46..b9a786e 100644
--- a/apps/web/src/components/ui/command.tsx
+++ b/packages/ui/src/components/ui/command.tsx
@@ -1,19 +1,19 @@
import * as React from "react"
import { Command as CommandPrimitive } from "cmdk"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
-} from "@/components/ui/dialog"
+} from "./dialog"
import {
InputGroup,
InputGroupAddon,
-} from "@/components/ui/input-group"
-import { MagnifierIcon, CheckIcon } from "@/components/icons"
+} from "./input-group"
+import { MagnifierIcon, CheckIcon } from "../icons"
function Command({
className,
diff --git a/apps/web/src/components/ui/context-menu.tsx b/packages/ui/src/components/ui/context-menu.tsx
similarity index 98%
rename from apps/web/src/components/ui/context-menu.tsx
rename to packages/ui/src/components/ui/context-menu.tsx
index 7891b30..ca25322 100644
--- a/apps/web/src/components/ui/context-menu.tsx
+++ b/packages/ui/src/components/ui/context-menu.tsx
@@ -3,8 +3,8 @@
import * as React from "react"
import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu"
-import { cn } from "@/lib/utils"
-import { ChevronRightIcon, CheckIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronRightIcon, CheckIcon } from "../icons"
function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/dialog.tsx b/packages/ui/src/components/ui/dialog.tsx
similarity index 96%
rename from apps/web/src/components/ui/dialog.tsx
rename to packages/ui/src/components/ui/dialog.tsx
index 94c559e..dba9fba 100644
--- a/apps/web/src/components/ui/dialog.tsx
+++ b/packages/ui/src/components/ui/dialog.tsx
@@ -3,9 +3,9 @@
import * as React from "react"
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { XmarkIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
+import { XmarkIcon } from "../icons"
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/direction.tsx b/packages/ui/src/components/ui/direction.tsx
similarity index 100%
rename from apps/web/src/components/ui/direction.tsx
rename to packages/ui/src/components/ui/direction.tsx
diff --git a/apps/web/src/components/ui/drawer.tsx b/packages/ui/src/components/ui/drawer.tsx
similarity index 99%
rename from apps/web/src/components/ui/drawer.tsx
rename to packages/ui/src/components/ui/drawer.tsx
index 34e2b77..ea96b19 100644
--- a/apps/web/src/components/ui/drawer.tsx
+++ b/packages/ui/src/components/ui/drawer.tsx
@@ -3,7 +3,7 @@
import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Drawer({
...props
diff --git a/apps/web/src/components/ui/dropdown-menu.tsx b/packages/ui/src/components/ui/dropdown-menu.tsx
similarity index 98%
rename from apps/web/src/components/ui/dropdown-menu.tsx
rename to packages/ui/src/components/ui/dropdown-menu.tsx
index 22e8adb..fa90972 100644
--- a/apps/web/src/components/ui/dropdown-menu.tsx
+++ b/packages/ui/src/components/ui/dropdown-menu.tsx
@@ -1,8 +1,8 @@
import * as React from "react"
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
-import { cn } from "@/lib/utils"
-import { ChevronRightIcon, CheckIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronRightIcon, CheckIcon } from "../icons"
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/empty.tsx b/packages/ui/src/components/ui/empty.tsx
similarity index 98%
rename from apps/web/src/components/ui/empty.tsx
rename to packages/ui/src/components/ui/empty.tsx
index 56b9661..be0f5b4 100644
--- a/apps/web/src/components/ui/empty.tsx
+++ b/packages/ui/src/components/ui/empty.tsx
@@ -1,6 +1,6 @@
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Empty({ className, ...props }: React.ComponentProps<"div">) {
return (
diff --git a/apps/web/src/components/ui/field.tsx b/packages/ui/src/components/ui/field.tsx
similarity index 97%
rename from apps/web/src/components/ui/field.tsx
rename to packages/ui/src/components/ui/field.tsx
index b81cab9..a6a656c 100644
--- a/apps/web/src/components/ui/field.tsx
+++ b/packages/ui/src/components/ui/field.tsx
@@ -3,9 +3,9 @@
import { useMemo } from "react"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { Label } from "@/components/ui/label"
-import { Separator } from "@/components/ui/separator"
+import { cn } from "../../lib/utils"
+import { Label } from "./label"
+import { Separator } from "./separator"
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
return (
diff --git a/apps/web/src/components/ui/gradient-chart.tsx b/packages/ui/src/components/ui/gradient-chart.tsx
similarity index 97%
rename from apps/web/src/components/ui/gradient-chart.tsx
rename to packages/ui/src/components/ui/gradient-chart.tsx
index 41a846b..d159cff 100644
--- a/apps/web/src/components/ui/gradient-chart.tsx
+++ b/packages/ui/src/components/ui/gradient-chart.tsx
@@ -6,7 +6,7 @@ import { Area, AreaChart } from "recharts";
import {
type ChartConfig,
ChartContainer,
-} from "@/components/ui/chart";
+} from "./chart";
interface SparklineProps {
data: { value: number }[];
diff --git a/apps/web/src/components/ui/hover-card.tsx b/packages/ui/src/components/ui/hover-card.tsx
similarity index 98%
rename from apps/web/src/components/ui/hover-card.tsx
rename to packages/ui/src/components/ui/hover-card.tsx
index 5ffbb34..59eeb2a 100644
--- a/apps/web/src/components/ui/hover-card.tsx
+++ b/packages/ui/src/components/ui/hover-card.tsx
@@ -2,7 +2,7 @@
import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/input-group.tsx b/packages/ui/src/components/ui/input-group.tsx
similarity index 96%
rename from apps/web/src/components/ui/input-group.tsx
rename to packages/ui/src/components/ui/input-group.tsx
index c49e22b..5947a8d 100644
--- a/apps/web/src/components/ui/input-group.tsx
+++ b/packages/ui/src/components/ui/input-group.tsx
@@ -1,10 +1,10 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
-import { Textarea } from "@/components/ui/textarea"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
+import { Input } from "./input"
+import { Textarea } from "./textarea"
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
diff --git a/apps/web/src/components/ui/input-otp.tsx b/packages/ui/src/components/ui/input-otp.tsx
similarity index 96%
rename from apps/web/src/components/ui/input-otp.tsx
rename to packages/ui/src/components/ui/input-otp.tsx
index 84c1338..6321216 100644
--- a/apps/web/src/components/ui/input-otp.tsx
+++ b/packages/ui/src/components/ui/input-otp.tsx
@@ -1,8 +1,8 @@
import * as React from "react"
import { OTPInput, OTPInputContext } from "input-otp"
-import { cn } from "@/lib/utils"
-import { MinusIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { MinusIcon } from "../icons"
function InputOTP({
className,
diff --git a/apps/web/src/components/ui/input.tsx b/packages/ui/src/components/ui/input.tsx
similarity index 96%
rename from apps/web/src/components/ui/input.tsx
rename to packages/ui/src/components/ui/input.tsx
index 52219c4..617c22c 100644
--- a/apps/web/src/components/ui/input.tsx
+++ b/packages/ui/src/components/ui/input.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
import { Input as InputPrimitive } from "@base-ui/react/input"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
diff --git a/apps/web/src/components/ui/item.tsx b/packages/ui/src/components/ui/item.tsx
similarity index 98%
rename from apps/web/src/components/ui/item.tsx
rename to packages/ui/src/components/ui/item.tsx
index f923049..5ec4920 100644
--- a/apps/web/src/components/ui/item.tsx
+++ b/packages/ui/src/components/ui/item.tsx
@@ -3,8 +3,8 @@ import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { Separator } from "@/components/ui/separator"
+import { cn } from "../../lib/utils"
+import { Separator } from "./separator"
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
diff --git a/apps/web/src/components/ui/kbd.tsx b/packages/ui/src/components/ui/kbd.tsx
similarity index 95%
rename from apps/web/src/components/ui/kbd.tsx
rename to packages/ui/src/components/ui/kbd.tsx
index 30efbc9..a909a35 100644
--- a/apps/web/src/components/ui/kbd.tsx
+++ b/packages/ui/src/components/ui/kbd.tsx
@@ -1,4 +1,4 @@
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
return (
diff --git a/apps/web/src/components/ui/label.tsx b/packages/ui/src/components/ui/label.tsx
similarity index 92%
rename from apps/web/src/components/ui/label.tsx
rename to packages/ui/src/components/ui/label.tsx
index 138bee1..0c0a993 100644
--- a/apps/web/src/components/ui/label.tsx
+++ b/packages/ui/src/components/ui/label.tsx
@@ -2,7 +2,7 @@
import * as React from "react"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Label({ className, ...props }: React.ComponentProps<"label">) {
return (
diff --git a/apps/web/src/components/ui/menubar.tsx b/packages/ui/src/components/ui/menubar.tsx
similarity index 98%
rename from apps/web/src/components/ui/menubar.tsx
rename to packages/ui/src/components/ui/menubar.tsx
index ac4b177..9cce45e 100644
--- a/apps/web/src/components/ui/menubar.tsx
+++ b/packages/ui/src/components/ui/menubar.tsx
@@ -2,7 +2,7 @@ import * as React from "react"
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
import {
DropdownMenu,
DropdownMenuContent,
@@ -17,8 +17,8 @@ import {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
-import { CheckIcon } from "@/components/icons"
+} from "./dropdown-menu"
+import { CheckIcon } from "../icons"
function Menubar({ className, ...props }: MenubarPrimitive.Props) {
return (
diff --git a/apps/web/src/components/ui/native-select.tsx b/packages/ui/src/components/ui/native-select.tsx
similarity index 95%
rename from apps/web/src/components/ui/native-select.tsx
rename to packages/ui/src/components/ui/native-select.tsx
index 2168b4d..df0ce0f 100644
--- a/apps/web/src/components/ui/native-select.tsx
+++ b/packages/ui/src/components/ui/native-select.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
-import { cn } from "@/lib/utils"
-import { ChevronExpandYIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronExpandYIcon } from "../icons"
type NativeSelectProps = Omit, "size"> & {
size?: "sm" | "default"
diff --git a/apps/web/src/components/ui/navigation-menu.tsx b/packages/ui/src/components/ui/navigation-menu.tsx
similarity index 98%
rename from apps/web/src/components/ui/navigation-menu.tsx
rename to packages/ui/src/components/ui/navigation-menu.tsx
index 88b4b84..9be1a2c 100644
--- a/apps/web/src/components/ui/navigation-menu.tsx
+++ b/packages/ui/src/components/ui/navigation-menu.tsx
@@ -1,8 +1,8 @@
import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu"
import { cva } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { ChevronDownIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronDownIcon } from "../icons"
function NavigationMenu({
align = "start",
diff --git a/apps/web/src/components/ui/pagination.tsx b/packages/ui/src/components/ui/pagination.tsx
similarity index 94%
rename from apps/web/src/components/ui/pagination.tsx
rename to packages/ui/src/components/ui/pagination.tsx
index 0a83690..b25a0a6 100644
--- a/apps/web/src/components/ui/pagination.tsx
+++ b/packages/ui/src/components/ui/pagination.tsx
@@ -1,8 +1,8 @@
import * as React from "react"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { ChevronLeftIcon, ChevronRightIcon, DotsIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
+import { ChevronLeftIcon, ChevronRightIcon, DotsIcon } from "../icons"
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return (
diff --git a/apps/web/src/components/ui/popover.tsx b/packages/ui/src/components/ui/popover.tsx
similarity index 98%
rename from apps/web/src/components/ui/popover.tsx
rename to packages/ui/src/components/ui/popover.tsx
index ee2f867..ada37f0 100644
--- a/apps/web/src/components/ui/popover.tsx
+++ b/packages/ui/src/components/ui/popover.tsx
@@ -1,7 +1,7 @@
import * as React from "react"
import { Popover as PopoverPrimitive } from "@base-ui/react/popover"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Popover({ ...props }: PopoverPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/progress.tsx b/packages/ui/src/components/ui/progress.tsx
similarity index 97%
rename from apps/web/src/components/ui/progress.tsx
rename to packages/ui/src/components/ui/progress.tsx
index f37167e..020933e 100644
--- a/apps/web/src/components/ui/progress.tsx
+++ b/packages/ui/src/components/ui/progress.tsx
@@ -2,7 +2,7 @@
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Progress({
className,
diff --git a/apps/web/src/components/ui/radio-group.tsx b/packages/ui/src/components/ui/radio-group.tsx
similarity index 94%
rename from apps/web/src/components/ui/radio-group.tsx
rename to packages/ui/src/components/ui/radio-group.tsx
index 9d47f74..7843146 100644
--- a/apps/web/src/components/ui/radio-group.tsx
+++ b/packages/ui/src/components/ui/radio-group.tsx
@@ -1,8 +1,8 @@
import { Radio as RadioPrimitive } from "@base-ui/react/radio"
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
-import { cn } from "@/lib/utils"
-import { RadioCheckedIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { RadioCheckedIcon } from "../icons"
function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
return (
diff --git a/apps/web/src/components/ui/resizable.tsx b/packages/ui/src/components/ui/resizable.tsx
similarity index 97%
rename from apps/web/src/components/ui/resizable.tsx
rename to packages/ui/src/components/ui/resizable.tsx
index cb8dabb..9f255f1 100644
--- a/apps/web/src/components/ui/resizable.tsx
+++ b/packages/ui/src/components/ui/resizable.tsx
@@ -2,7 +2,7 @@
import * as ResizablePrimitive from "react-resizable-panels"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function ResizablePanelGroup({
className,
diff --git a/apps/web/src/components/ui/scroll-area.tsx b/packages/ui/src/components/ui/scroll-area.tsx
similarity index 97%
rename from apps/web/src/components/ui/scroll-area.tsx
rename to packages/ui/src/components/ui/scroll-area.tsx
index 8a90afb..56d25da 100644
--- a/apps/web/src/components/ui/scroll-area.tsx
+++ b/packages/ui/src/components/ui/scroll-area.tsx
@@ -1,6 +1,6 @@
import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function ScrollArea({
className,
diff --git a/apps/web/src/components/ui/select.tsx b/packages/ui/src/components/ui/select.tsx
similarity index 98%
rename from apps/web/src/components/ui/select.tsx
rename to packages/ui/src/components/ui/select.tsx
index 3d13e83..ce54d7e 100644
--- a/apps/web/src/components/ui/select.tsx
+++ b/packages/ui/src/components/ui/select.tsx
@@ -3,8 +3,8 @@
import * as React from "react"
import { Select as SelectPrimitive } from "@base-ui/react/select"
-import { cn } from "@/lib/utils"
-import { ChevronExpandYIcon, CheckIcon, ChevronUpIcon, ChevronDownIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { ChevronExpandYIcon, CheckIcon, ChevronUpIcon, ChevronDownIcon } from "../icons"
const Select = SelectPrimitive.Root
diff --git a/apps/web/src/components/ui/separator.tsx b/packages/ui/src/components/ui/separator.tsx
similarity index 93%
rename from apps/web/src/components/ui/separator.tsx
rename to packages/ui/src/components/ui/separator.tsx
index 3703d32..b44d459 100644
--- a/apps/web/src/components/ui/separator.tsx
+++ b/packages/ui/src/components/ui/separator.tsx
@@ -1,6 +1,6 @@
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Separator({
className,
diff --git a/apps/web/src/components/ui/sheet.tsx b/packages/ui/src/components/ui/sheet.tsx
similarity index 97%
rename from apps/web/src/components/ui/sheet.tsx
rename to packages/ui/src/components/ui/sheet.tsx
index c84658e..fc3a86a 100644
--- a/apps/web/src/components/ui/sheet.tsx
+++ b/packages/ui/src/components/ui/sheet.tsx
@@ -1,9 +1,9 @@
import * as React from "react"
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { XmarkIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
+import { XmarkIcon } from "../icons"
function Sheet({ ...props }: SheetPrimitive.Root.Props) {
return
diff --git a/apps/web/src/components/ui/sidebar.tsx b/packages/ui/src/components/ui/sidebar.tsx
similarity index 98%
rename from apps/web/src/components/ui/sidebar.tsx
rename to packages/ui/src/components/ui/sidebar.tsx
index 3a38dea..6ce2007 100644
--- a/apps/web/src/components/ui/sidebar.tsx
+++ b/packages/ui/src/components/ui/sidebar.tsx
@@ -3,25 +3,25 @@ import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
-import { Separator } from "@/components/ui/separator"
+import { cn } from "../../lib/utils"
+import { Button } from "./button"
+import { Input } from "./input"
+import { Separator } from "./separator"
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
-} from "@/components/ui/sheet"
-import { Skeleton } from "@/components/ui/skeleton"
+} from "./sheet"
+import { Skeleton } from "./skeleton"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
-} from "@/components/ui/tooltip"
-import { useIsMobile } from "@/hooks/use-mobile"
-import { SidebarLeftIcon } from "@/components/icons"
+} from "./tooltip"
+import { useIsMobile } from "../../hooks/use-mobile"
+import { SidebarLeftIcon } from "../icons"
const SIDEBAR_STORAGE_KEY = "maple-sidebar-expanded"
const SIDEBAR_WIDTH = "16rem"
diff --git a/apps/web/src/components/ui/skeleton.tsx b/packages/ui/src/components/ui/skeleton.tsx
similarity index 86%
rename from apps/web/src/components/ui/skeleton.tsx
rename to packages/ui/src/components/ui/skeleton.tsx
index f768775..38a8037 100644
--- a/apps/web/src/components/ui/skeleton.tsx
+++ b/packages/ui/src/components/ui/skeleton.tsx
@@ -1,4 +1,4 @@
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
diff --git a/apps/web/src/components/ui/slider.tsx b/packages/ui/src/components/ui/slider.tsx
similarity index 98%
rename from apps/web/src/components/ui/slider.tsx
rename to packages/ui/src/components/ui/slider.tsx
index 55b56be..a1ab3db 100644
--- a/apps/web/src/components/ui/slider.tsx
+++ b/packages/ui/src/components/ui/slider.tsx
@@ -3,7 +3,7 @@
import * as React from "react"
import { Slider as SliderPrimitive } from "@base-ui/react/slider"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Slider({
className,
diff --git a/apps/web/src/components/ui/sonner.tsx b/packages/ui/src/components/ui/sonner.tsx
similarity index 96%
rename from apps/web/src/components/ui/sonner.tsx
rename to packages/ui/src/components/ui/sonner.tsx
index 0302475..a056f82 100644
--- a/apps/web/src/components/ui/sonner.tsx
+++ b/packages/ui/src/components/ui/sonner.tsx
@@ -1,6 +1,6 @@
import { useTheme } from "next-themes"
import { Toaster as Sonner, type ToasterProps } from "sonner"
-import { CircleCheckIcon, CircleInfoIcon, AlertWarningIcon, CircleXmarkIcon, LoaderIcon } from "@/components/icons"
+import { CircleCheckIcon, CircleInfoIcon, AlertWarningIcon, CircleXmarkIcon, LoaderIcon } from "../icons"
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme()
diff --git a/apps/web/src/components/ui/spinner.tsx b/packages/ui/src/components/ui/spinner.tsx
similarity index 80%
rename from apps/web/src/components/ui/spinner.tsx
rename to packages/ui/src/components/ui/spinner.tsx
index 990173c..ab5bbc0 100644
--- a/apps/web/src/components/ui/spinner.tsx
+++ b/packages/ui/src/components/ui/spinner.tsx
@@ -1,5 +1,5 @@
-import { cn } from "@/lib/utils"
-import { LoaderIcon } from "@/components/icons"
+import { cn } from "../../lib/utils"
+import { LoaderIcon } from "../icons"
function Spinner({
className,
diff --git a/apps/web/src/components/ui/switch.tsx b/packages/ui/src/components/ui/switch.tsx
similarity index 97%
rename from apps/web/src/components/ui/switch.tsx
rename to packages/ui/src/components/ui/switch.tsx
index cfa13af..d474a0f 100644
--- a/apps/web/src/components/ui/switch.tsx
+++ b/packages/ui/src/components/ui/switch.tsx
@@ -2,7 +2,7 @@
import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Switch({
className,
diff --git a/apps/web/src/components/ui/table.tsx b/packages/ui/src/components/ui/table.tsx
similarity index 98%
rename from apps/web/src/components/ui/table.tsx
rename to packages/ui/src/components/ui/table.tsx
index 4886601..6e6de38 100644
--- a/apps/web/src/components/ui/table.tsx
+++ b/packages/ui/src/components/ui/table.tsx
@@ -1,6 +1,6 @@
import * as React from "react"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Table({ className, ...props }: React.ComponentProps<"table">) {
return (
diff --git a/apps/web/src/components/ui/tabs.tsx b/packages/ui/src/components/ui/tabs.tsx
similarity index 98%
rename from apps/web/src/components/ui/tabs.tsx
rename to packages/ui/src/components/ui/tabs.tsx
index 3200099..429730e 100644
--- a/apps/web/src/components/ui/tabs.tsx
+++ b/packages/ui/src/components/ui/tabs.tsx
@@ -3,7 +3,7 @@
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function Tabs({
className,
diff --git a/apps/web/src/components/ui/textarea.tsx b/packages/ui/src/components/ui/textarea.tsx
similarity index 96%
rename from apps/web/src/components/ui/textarea.tsx
rename to packages/ui/src/components/ui/textarea.tsx
index 1692ac9..51d157a 100644
--- a/apps/web/src/components/ui/textarea.tsx
+++ b/packages/ui/src/components/ui/textarea.tsx
@@ -1,6 +1,6 @@
import * as React from "react"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
const Textarea = React.forwardRef>(
({ className, ...props }, ref) => {
diff --git a/apps/web/src/components/ui/toggle-group.tsx b/packages/ui/src/components/ui/toggle-group.tsx
similarity index 97%
rename from apps/web/src/components/ui/toggle-group.tsx
rename to packages/ui/src/components/ui/toggle-group.tsx
index 4fb45ba..51b5a7a 100644
--- a/apps/web/src/components/ui/toggle-group.tsx
+++ b/packages/ui/src/components/ui/toggle-group.tsx
@@ -5,8 +5,8 @@ import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
import { type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
-import { toggleVariants } from "@/components/ui/toggle"
+import { cn } from "../../lib/utils"
+import { toggleVariants } from "./toggle"
const ToggleGroupContext = React.createContext<
VariantProps & {
diff --git a/apps/web/src/components/ui/toggle.tsx b/packages/ui/src/components/ui/toggle.tsx
similarity index 97%
rename from apps/web/src/components/ui/toggle.tsx
rename to packages/ui/src/components/ui/toggle.tsx
index 6ef1f5d..6986473 100644
--- a/apps/web/src/components/ui/toggle.tsx
+++ b/packages/ui/src/components/ui/toggle.tsx
@@ -1,7 +1,7 @@
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
const toggleVariants = cva(
"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[state=on]:bg-muted gap-1 rounded-none text-xs font-medium transition-all [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
diff --git a/apps/web/src/components/ui/tooltip.tsx b/packages/ui/src/components/ui/tooltip.tsx
similarity index 98%
rename from apps/web/src/components/ui/tooltip.tsx
rename to packages/ui/src/components/ui/tooltip.tsx
index 6c608e2..11cd2d7 100644
--- a/apps/web/src/components/ui/tooltip.tsx
+++ b/packages/ui/src/components/ui/tooltip.tsx
@@ -2,7 +2,7 @@
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
-import { cn } from "@/lib/utils"
+import { cn } from "../../lib/utils"
function TooltipProvider({
delay = 0,
diff --git a/packages/ui/src/env.d.ts b/packages/ui/src/env.d.ts
new file mode 100644
index 0000000..2eeb318
--- /dev/null
+++ b/packages/ui/src/env.d.ts
@@ -0,0 +1,4 @@
+declare module "*.css" {
+ const content: string
+ export default content
+}
diff --git a/packages/ui/src/hooks/use-mobile.tsx b/packages/ui/src/hooks/use-mobile.tsx
new file mode 100644
index 0000000..2b0fe1d
--- /dev/null
+++ b/packages/ui/src/hooks/use-mobile.tsx
@@ -0,0 +1,19 @@
+import * as React from "react"
+
+const MOBILE_BREAKPOINT = 768
+
+export function useIsMobile() {
+ const [isMobile, setIsMobile] = React.useState(undefined)
+
+ React.useEffect(() => {
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
+ const onChange = () => {
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ }
+ mql.addEventListener("change", onChange)
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ return () => mql.removeEventListener("change", onChange)
+ }, [])
+
+ return !!isMobile
+}
diff --git a/apps/web/src/lib/colors.ts b/packages/ui/src/lib/colors.ts
similarity index 100%
rename from apps/web/src/lib/colors.ts
rename to packages/ui/src/lib/colors.ts
diff --git a/packages/ui/src/lib/format.ts b/packages/ui/src/lib/format.ts
new file mode 100644
index 0000000..9e9b8fc
--- /dev/null
+++ b/packages/ui/src/lib/format.ts
@@ -0,0 +1,165 @@
+/**
+ * Format a duration in milliseconds to a human-readable string.
+ * - < 1ms: displays in microseconds (μs)
+ * - 1ms - 1000ms: displays in milliseconds (ms)
+ * - >= 1000ms: displays in seconds (s)
+ */
+export function formatDuration(ms: number): string {
+ if (ms < 1) {
+ return `${(ms * 1000).toFixed(0)}μs`
+ }
+ if (ms < 1000) {
+ return `${ms.toFixed(1)}ms`
+ }
+ return `${(ms / 1000).toFixed(2)}s`
+}
+
+/**
+ * Format a number with compact notation.
+ * - >= 1M: displays as e.g. "1.2M"
+ * - >= 1K: displays as e.g. "3.4K"
+ * - < 1K: displays with locale formatting
+ */
+export function formatNumber(num: number): string {
+ if (num >= 1_000_000) {
+ return `${(num / 1_000_000).toFixed(1)}M`
+ }
+ if (num >= 1_000) {
+ return `${(num / 1_000).toFixed(1)}K`
+ }
+ return num.toLocaleString()
+}
+
+/**
+ * Format a latency value in milliseconds to a human-readable string.
+ */
+export function formatLatency(ms: number): string {
+ if (ms == null || Number.isNaN(ms)) {
+ return "-"
+ }
+ if (ms < 1) {
+ return `${(ms * 1000).toFixed(0)}μs`
+ }
+ if (ms < 1000) {
+ return `${ms.toFixed(1)}ms`
+ }
+ return `${(ms / 1000).toFixed(2)}s`
+}
+
+/**
+ * Format an error rate percentage.
+ */
+export function formatErrorRate(rate: number): string {
+ if (rate < 0.01) {
+ return "0%"
+ }
+ if (rate < 1) {
+ return `${rate.toFixed(2)}%`
+ }
+ return `${rate.toFixed(1)}%`
+}
+
+/**
+ * Infer the bucket interval in seconds from consecutive data points.
+ * Expects data with a `bucket` string timestamp field.
+ */
+export function inferBucketSeconds(data: Array<{ bucket: string }>): number | undefined {
+ if (data.length < 2) return undefined
+ const t0 = new Date(data[0].bucket).getTime()
+ const t1 = new Date(data[1].bucket).getTime()
+ const diffMs = t1 - t0
+ if (diffMs <= 0 || Number.isNaN(diffMs)) return undefined
+ return diffMs / 1000
+}
+
+/**
+ * Parse a bucket value to a millisecond timestamp.
+ */
+export function parseBucketMs(value: unknown): number | null {
+ if (typeof value !== "string") return null
+ const parsed = new Date(value).getTime()
+ return Number.isNaN(parsed) ? null : parsed
+}
+
+/**
+ * Infer the total time range in milliseconds from an array of data points with a `bucket` key.
+ */
+export function inferRangeMs(data: Array>): number {
+ const bucketTimes = data
+ .map((row) => parseBucketMs(row.bucket))
+ .filter((value): value is number => value != null)
+
+ if (bucketTimes.length < 2) return 0
+ return Math.max(...bucketTimes) - Math.min(...bucketTimes)
+}
+
+/**
+ * Format a bucket timestamp label that adapts based on the overall time range:
+ * - >= 24h with daily buckets: "Feb 14"
+ * - >= 24h with sub-day buckets: "Feb 14, 02:00 PM"
+ * - 30min - 24h: "02:00 PM"
+ * - <= 30min: "02:00:30 PM"
+ */
+export function formatBucketLabel(
+ value: unknown,
+ context: { rangeMs: number; bucketSeconds: number | undefined },
+ mode: "tick" | "tooltip",
+): string {
+ if (typeof value !== "string") return ""
+
+ const date = new Date(value)
+ if (Number.isNaN(date.getTime())) return value
+
+ const includeDate = context.rangeMs >= 24 * 60 * 60 * 1000 || (context.bucketSeconds ?? 0) >= 24 * 60 * 60
+ const includeSeconds = context.rangeMs <= 30 * 60 * 1000 && !includeDate
+
+ if (mode === "tooltip") {
+ return date.toLocaleString(undefined, {
+ year: includeDate ? "numeric" : undefined,
+ month: includeDate ? "short" : undefined,
+ day: includeDate ? "numeric" : undefined,
+ hour: "2-digit",
+ minute: "2-digit",
+ second: includeSeconds ? "2-digit" : undefined,
+ })
+ }
+
+ if (includeDate) {
+ if ((context.bucketSeconds ?? 0) >= 24 * 60 * 60) {
+ return date.toLocaleDateString(undefined, { month: "short", day: "numeric" })
+ }
+ return date.toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" })
+ }
+
+ return date
+ .toLocaleTimeString(undefined, {
+ hour: "2-digit",
+ minute: "2-digit",
+ second: includeSeconds ? "2-digit" : undefined,
+ })
+ .replace(/^24:/, "00:")
+}
+
+const bucketLabelMap: Record = {
+ 60: "/min",
+ 300: "/5min",
+ 900: "/15min",
+ 3600: "/h",
+ 14400: "/4h",
+ 86400: "/d",
+}
+
+/**
+ * Map bucket interval seconds to a human-readable rate suffix.
+ */
+export function bucketIntervalLabel(seconds: number | undefined): string {
+ if (seconds == null) return ""
+ return bucketLabelMap[seconds] ?? ""
+}
+
+/**
+ * Format a throughput value with a rate suffix for chart axes.
+ */
+export function formatThroughput(value: number, suffix: string): string {
+ return `${formatNumber(value)}${suffix}`
+}
diff --git a/apps/landing/src/lib/types.ts b/packages/ui/src/lib/types.ts
similarity index 100%
rename from apps/landing/src/lib/types.ts
rename to packages/ui/src/lib/types.ts
diff --git a/apps/landing/src/lib/utils.ts b/packages/ui/src/lib/utils.ts
similarity index 100%
rename from apps/landing/src/lib/utils.ts
rename to packages/ui/src/lib/utils.ts
diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json
new file mode 100644
index 0000000..7e4b9b5
--- /dev/null
+++ b/packages/ui/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "include": ["**/*.ts", "**/*.tsx"],
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "jsx": "react-jsx",
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ }
+}
From a6b29f6eeb7a0ec718b0de9a096f539d9e0d4901 Mon Sep 17 00:00:00 2001
From: Makisuo
Date: Fri, 20 Feb 2026 01:37:21 +0100
Subject: [PATCH 2/5] stuff
---
.../landing/src/components/ServiceMapMock.tsx | 437 ++++++++++++++++++
.../src/components/UseCaseShowcase.astro | 44 +-
.../src/pages/features/service-catalog.astro | 40 +-
.../use-cases/microservices-debugging.astro | 49 +-
apps/web/src/api/tinybird/traces.test.ts | 95 ++++
apps/web/src/api/tinybird/traces.ts | 12 +
.../query-builder/where-clause-editor.tsx | 6 +-
.../traces/traces-filter-sidebar.tsx | 150 +++++-
.../src/components/traces/traces-table.tsx | 2 +
.../where-clause-autocomplete.test.ts | 47 ++
.../where-clause-autocomplete.ts | 120 ++++-
.../lib/traces/advanced-filter-sync.test.ts | 111 +++++
.../src/lib/traces/advanced-filter-sync.ts | 375 +++++++++++++++
apps/web/src/routes/traces/index.tsx | 28 +-
packages/domain/src/tinybird/endpoints.ts | 59 +++
15 files changed, 1422 insertions(+), 153 deletions(-)
create mode 100644 apps/landing/src/components/ServiceMapMock.tsx
create mode 100644 apps/web/src/api/tinybird/traces.test.ts
create mode 100644 apps/web/src/lib/traces/advanced-filter-sync.test.ts
create mode 100644 apps/web/src/lib/traces/advanced-filter-sync.ts
diff --git a/apps/landing/src/components/ServiceMapMock.tsx b/apps/landing/src/components/ServiceMapMock.tsx
new file mode 100644
index 0000000..95999dc
--- /dev/null
+++ b/apps/landing/src/components/ServiceMapMock.tsx
@@ -0,0 +1,437 @@
+import { memo, useId } from "react"
+import {
+ ReactFlow,
+ Background,
+ BackgroundVariant,
+ Handle,
+ Position,
+ getSmoothStepPath,
+ type EdgeProps,
+ type NodeProps,
+ type Node,
+ type Edge,
+} from "@xyflow/react"
+import "@xyflow/react/dist/style.css"
+import { cn } from "@maple/ui/utils"
+import { getServiceLegendColor } from "@maple/ui/colors"
+
+// --- TYPES ---
+
+interface MockNodeData extends Record {
+ label: string
+ throughput: number
+ errorRate: number
+ avgLatencyMs: number
+ services: string[]
+}
+
+interface MockEdgeData extends Record {
+ callCount: number
+ callsPerSecond: number
+ errorRate: number
+ services: string[]
+}
+
+// --- UTILS ---
+
+function formatRate(value: number): string {
+ if (value >= 1000) return `${(value / 1000).toFixed(1)}k`
+ if (value >= 1) return value.toFixed(1)
+ return value.toFixed(2)
+}
+
+function formatLatency(ms: number): string {
+ if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`
+ return `${ms.toFixed(0)}ms`
+}
+
+function getHealthClass(errorRate: number): string {
+ if (errorRate > 5) return "ring-red-500/60"
+ if (errorRate > 1) return "ring-amber-500/60"
+ return "ring-emerald-500/40"
+}
+
+function getHealthDotClass(errorRate: number): string {
+ if (errorRate > 5) return "bg-red-500"
+ if (errorRate > 1) return "bg-amber-500"
+ return "bg-emerald-500"
+}
+
+// --- CUSTOM NODE ---
+
+const MockNode = memo(function MockNode({ data }: NodeProps) {
+ const mockData = data as unknown as MockNodeData
+ const { label, throughput, errorRate, avgLatencyMs, services } = mockData
+ const color = getServiceLegendColor(label, services)
+
+ return (
+ <>
+
+
+
+
+
+
req/s
+
+ {formatRate(throughput)}
+
+
+
+
P95
+
5
+ ? "text-red-600 dark:text-red-400"
+ : errorRate > 1
+ ? "text-amber-600 dark:text-amber-400"
+ : "text-foreground",
+ )}
+ >
+ {formatLatency(avgLatencyMs)}
+
+
+
+
+
+ >
+ )
+})
+
+// --- CUSTOM EDGE ---
+
+function getStrokeWidth(callCount: number): number {
+ if (callCount <= 0) return 2
+ return Math.min(6, Math.max(2, 2 + Math.log10(callCount) * 1.5))
+}
+
+function getEdgeIntensity(callsPerSecond: number): number {
+ if (callsPerSecond <= 0) return 0.15
+ return Math.min(1, 0.3 + 0.7 * (Math.log10(1 + callsPerSecond) / Math.log10(100)))
+}
+
+function simpleHash(str: string): number {
+ let h = 0
+ for (let i = 0; i < str.length; i++) {
+ h = (h * 31 + str.charCodeAt(i)) | 0
+ }
+ return (Math.abs(h) % 1000) / 1000
+}
+
+const TRAVERSE_TIME = 2
+const MAX_DUR = 20
+const MAX_PARTICLES = 5
+
+const MockEdge = memo(function MockEdge({
+ id,
+ source,
+ target,
+ sourceX,
+ sourceY,
+ targetX,
+ targetY,
+ sourcePosition,
+ targetPosition,
+ data,
+}: EdgeProps) {
+ const uniqueId = useId()
+ const edgeData = data as unknown as MockEdgeData
+
+ const callCount = edgeData?.callCount ?? 0
+ const callsPerSecond = edgeData?.callsPerSecond ?? 0
+ const services = edgeData?.services ?? []
+
+ const [edgePath] = getSmoothStepPath({
+ sourceX,
+ sourceY,
+ targetX,
+ targetY,
+ sourcePosition,
+ targetPosition,
+ borderRadius: 12,
+ })
+
+ const sourceColor = getServiceLegendColor(source, services)
+ const targetColor = getServiceLegendColor(target, services)
+ const sw = getStrokeWidth(callCount)
+ const i = getEdgeIntensity(callsPerSecond)
+
+ const rate = Math.max(callsPerSecond, 0)
+ let particleCount: number
+ let traversalDuration: number
+
+ if (rate <= 0) {
+ particleCount = 0
+ traversalDuration = TRAVERSE_TIME
+ } else {
+ const interArrival = 1 / rate
+ if (interArrival > TRAVERSE_TIME) {
+ particleCount = 1
+ traversalDuration = Math.min(interArrival, MAX_DUR)
+ } else {
+ traversalDuration = TRAVERSE_TIME
+ particleCount = Math.min(MAX_PARTICLES, Math.max(1, Math.round(rate * TRAVERSE_TIME)))
+ }
+ }
+
+ const stagger = traversalDuration / particleCount
+ const edgeOffset = simpleHash(id) * Math.min(stagger, 1)
+ const particleRadius = Math.max(2, sw * 0.6)
+
+ const safeId = `${id}-${uniqueId}`.replace(/[^a-zA-Z0-9-_]/g, "_")
+ const pathId = `path-${safeId}`
+ const gradientId = `grad-${safeId}`
+ const ambientFilterId = `ambient-${safeId}`
+ const glassFilterId = `glass-${safeId}`
+ const bloomFilterId = `bloom-${safeId}`
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: particleCount }).map((_, idx) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+ >
+ )
+})
+
+// --- DATA ---
+
+const ALL_SERVICES = ["api-gateway", "auth-svc", "order-svc", "payment-svc", "user-db"]
+
+const initialNodes = [
+ {
+ id: "api-gateway",
+ type: "serviceNode",
+ position: { x: 180, y: 20 },
+ data: { label: "api-gateway", throughput: 1200, errorRate: 0, avgLatencyMs: 480, services: ALL_SERVICES },
+ },
+ {
+ id: "auth-svc",
+ type: "serviceNode",
+ position: { x: 0, y: 140 },
+ data: { label: "auth-svc", throughput: 850, errorRate: 0, avgLatencyMs: 22, services: ALL_SERVICES },
+ },
+ {
+ id: "order-svc",
+ type: "serviceNode",
+ position: { x: 180, y: 140 },
+ data: { label: "order-svc", throughput: 420, errorRate: 2, avgLatencyMs: 85, services: ALL_SERVICES },
+ },
+ {
+ id: "payment-svc",
+ type: "serviceNode",
+ position: { x: 360, y: 140 },
+ data: { label: "payment-svc", throughput: 120, errorRate: 4, avgLatencyMs: 280, services: ALL_SERVICES },
+ },
+ {
+ id: "user-db",
+ type: "serviceNode",
+ position: { x: 180, y: 260 },
+ data: { label: "user-db", throughput: 1800, errorRate: 100, avgLatencyMs: 450, services: ALL_SERVICES },
+ },
+]
+
+const initialEdges = [
+ {
+ id: "api-auth",
+ source: "api-gateway",
+ target: "auth-svc",
+ type: "serviceEdge",
+ data: { callCount: 850, callsPerSecond: 14, errorRate: 0, services: ALL_SERVICES },
+ },
+ {
+ id: "api-order",
+ source: "api-gateway",
+ target: "order-svc",
+ type: "serviceEdge",
+ data: { callCount: 420, callsPerSecond: 7, errorRate: 2, services: ALL_SERVICES },
+ },
+ {
+ id: "api-payment",
+ source: "api-gateway",
+ target: "payment-svc",
+ type: "serviceEdge",
+ data: { callCount: 120, callsPerSecond: 2, errorRate: 4, services: ALL_SERVICES },
+ },
+ {
+ id: "auth-db",
+ source: "auth-svc",
+ target: "user-db",
+ type: "serviceEdge",
+ data: { callCount: 850, callsPerSecond: 14, errorRate: 100, services: ALL_SERVICES },
+ },
+ {
+ id: "order-db",
+ source: "order-svc",
+ target: "user-db",
+ type: "serviceEdge",
+ data: { callCount: 840, callsPerSecond: 14, errorRate: 100, services: ALL_SERVICES },
+ },
+ {
+ id: "payment-db",
+ source: "payment-svc",
+ target: "user-db",
+ type: "serviceEdge",
+ data: { callCount: 120, callsPerSecond: 2, errorRate: 100, services: ALL_SERVICES },
+ },
+]
+
+const nodeTypes = { serviceNode: MockNode }
+const edgeTypes = { serviceEdge: MockEdge }
+
+export function ServiceMapMock() {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/landing/src/components/UseCaseShowcase.astro b/apps/landing/src/components/UseCaseShowcase.astro
index 2f691bd..4a02a80 100644
--- a/apps/landing/src/components/UseCaseShowcase.astro
+++ b/apps/landing/src/components/UseCaseShowcase.astro
@@ -1,5 +1,5 @@
---
-
+import { ServiceMapMock } from "./ServiceMapMock.tsx";
---
@@ -82,46 +82,8 @@
Service Map
-