Skip to content

Commit d4cc630

Browse files
WellDunDunclaudegithub-actions[bot]
authored
feat: extract @selftune/ui shared component package (#61)
* feat: extract @selftune/ui shared component package Move 11 shadcn/ui primitives and 7 domain components from apps/local-dashboard into packages/ui/ as a source-only workspace package. Rewire dashboard imports to consume from @selftune/ui. Add packages/ui/README.md and update system-overview design doc. - SkillHealthGrid now accepts renderSkillName prop (decouples react-router) - Root workspaces expanded to include apps/* for workspace resolution - Dashboard types.ts, utils.ts, constants.tsx re-export from shared package - Build, tests (1398/1398), and lint all pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: bump cli version to v0.2.4 [skip ci] * fix: address CodeRabbit review feedback - Consolidate four separate @selftune/ui/components imports into one (Overview.tsx) - Align index.md file-level verification date to 2026-03-16 - Guard timeAgo() against invalid timestamps returning NaN - Broaden renderSkillName prop to accept full SkillCard instead of just name - Add type-parity-check.ts compile-time guard against contract drift Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: format type-parity-check.ts for biome Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address second round of CodeRabbit review - Guard lastSeen sort comparator against NaN from invalid timestamps - Guard formatRate against NaN/Infinity inputs - Move type-parity-check from packages/ui to tests/types/ (preserves UI package as a leaf with no CLI dependency) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: bump cli version to v0.2.5 [skip ci] --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 9c5753c commit d4cc630

47 files changed

Lines changed: 2123 additions & 356 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/local-dashboard/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"tailwind-merge": "^3.5.0",
3232
"tw-animate-css": "^1.4.0",
3333
"vaul": "^1.1.2",
34-
"zod": "^4.3.6"
34+
"zod": "^4.3.6",
35+
"@selftune/ui": "workspace:*"
3536
},
3637
"devDependencies": {
3738
"@types/react": "^19.1.6",

apps/local-dashboard/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import { AppSidebar } from "@/components/app-sidebar"
55
import { SiteHeader } from "@/components/site-header"
66
import { ThemeProvider } from "@/components/theme-provider"
77
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"
8-
import { TooltipProvider } from "@/components/ui/tooltip"
8+
import { TooltipProvider } from "@selftune/ui/primitives"
99
import { Overview } from "@/pages/Overview"
1010
import { SkillReport } from "@/pages/SkillReport"
1111
import { Status } from "@/pages/Status"
1212
import { useOverview } from "@/hooks/useOverview"
1313
import type { SkillHealthStatus, SkillSummary } from "@/types"
14-
import { deriveStatus, sortByPassRateAndChecks } from "@/utils"
14+
import { deriveStatus, sortByPassRateAndChecks } from "@selftune/ui/lib"
1515

1616
const queryClient = new QueryClient({
1717
defaultOptions: {

apps/local-dashboard/src/components/app-sidebar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useEffect, useMemo, useState } from "react"
22
import { Link, useLocation } from "react-router-dom"
3-
import { Badge } from "@/components/ui/badge"
43
import {
4+
Badge,
55
Collapsible,
66
CollapsibleContent,
77
CollapsibleTrigger,
8-
} from "@/components/ui/collapsible"
8+
} from "@selftune/ui/primitives"
99
import { Input } from "@/components/ui/input"
1010
import {
1111
Sidebar,
@@ -35,7 +35,7 @@ import {
3535
SearchIcon,
3636
XCircleIcon,
3737
} from "lucide-react"
38-
import { formatRate } from "@/utils"
38+
import { formatRate } from "@selftune/ui/lib"
3939
import type { SkillHealthStatus } from "@/types"
4040

4141
interface SkillNavItem {

apps/local-dashboard/src/components/theme-toggle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MoonIcon, SunIcon } from "lucide-react"
2-
import { Button } from "@/components/ui/button"
2+
import { Button } from "@selftune/ui/primitives"
33
import { useTheme } from "@/components/theme-provider"
44

55
export function ThemeToggle() {

apps/local-dashboard/src/components/ui/sheet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as React from "react"
44
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
55

66
import { cn } from "@/lib/utils"
7-
import { Button } from "@/components/ui/button"
7+
import { Button } from "@selftune/ui/primitives"
88
import { XIcon } from "lucide-react"
99

1010
function Sheet({ ...props }: SheetPrimitive.Root.Props) {

apps/local-dashboard/src/components/ui/sidebar.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import { cva, type VariantProps } from "class-variance-authority"
55

66
import { useIsMobile } from "@/hooks/use-mobile"
77
import { cn } from "@/lib/utils"
8-
import { Button } from "@/components/ui/button"
8+
import {
9+
Button,
10+
Tooltip,
11+
TooltipContent,
12+
TooltipTrigger,
13+
} from "@selftune/ui/primitives"
914
import { Input } from "@/components/ui/input"
1015
import { Separator } from "@/components/ui/separator"
1116
import {
@@ -16,11 +21,6 @@ import {
1621
SheetTitle,
1722
} from "@/components/ui/sheet"
1823
import { Skeleton } from "@/components/ui/skeleton"
19-
import {
20-
Tooltip,
21-
TooltipContent,
22-
TooltipTrigger,
23-
} from "@/components/ui/tooltip"
2424
import { PanelLeftIcon } from "lucide-react"
2525

2626
const SIDEBAR_COOKIE_NAME = "sidebar_state"
Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,2 @@
1-
import {
2-
AlertTriangleIcon,
3-
CheckCircleIcon,
4-
CircleDotIcon,
5-
HelpCircleIcon,
6-
XCircleIcon,
7-
} from "lucide-react";
8-
import type { SkillHealthStatus } from "./types";
9-
10-
export const STATUS_CONFIG: Record<
11-
SkillHealthStatus,
12-
{
13-
icon: React.ReactNode;
14-
variant: "default" | "secondary" | "destructive" | "outline";
15-
label: string;
16-
}
17-
> = {
18-
HEALTHY: {
19-
icon: <CheckCircleIcon className="size-4 text-emerald-600" />,
20-
variant: "outline",
21-
label: "Healthy",
22-
},
23-
WARNING: {
24-
icon: <AlertTriangleIcon className="size-4 text-amber-500" />,
25-
variant: "secondary",
26-
label: "Warning",
27-
},
28-
CRITICAL: {
29-
icon: <XCircleIcon className="size-4 text-red-500" />,
30-
variant: "destructive",
31-
label: "Critical",
32-
},
33-
UNGRADED: {
34-
icon: <CircleDotIcon className="size-4 text-muted-foreground" />,
35-
variant: "secondary",
36-
label: "Ungraded",
37-
},
38-
UNKNOWN: {
39-
icon: <HelpCircleIcon className="size-4 text-muted-foreground/60" />,
40-
variant: "secondary",
41-
label: "Unknown",
42-
},
43-
};
1+
// Re-export from shared package
2+
export { STATUS_CONFIG } from "@selftune/ui/lib";
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
import { type ClassValue, clsx } from "clsx";
2-
import { twMerge } from "tailwind-merge";
3-
4-
export function cn(...inputs: ClassValue[]) {
5-
return twMerge(clsx(inputs));
6-
}
1+
export { cn } from "@selftune/ui/lib";

apps/local-dashboard/src/pages/Overview.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { useMemo, useState } from "react"
2-
import { ActivityPanel } from "@/components/ActivityTimeline"
3-
import { OrchestrateRunsPanel } from "@/components/OrchestrateRunsPanel"
4-
import { SectionCards } from "@/components/section-cards"
5-
import { SkillHealthGrid } from "@/components/skill-health-grid"
2+
import { Link } from "react-router-dom"
3+
import {
4+
ActivityPanel,
5+
OrchestrateRunsPanel,
6+
SectionCards,
7+
SkillHealthGrid,
8+
} from "@selftune/ui/components"
69
import type { UseQueryResult } from "@tanstack/react-query"
710
import type { SkillCard, SkillHealthStatus, SkillSummary, OverviewResponse } from "@/types"
811
import { useOrchestrateRuns } from "@/hooks/useOrchestrateRuns"
912
import { deriveStatus, sortByPassRateAndChecks } from "@/utils"
1013
import { Skeleton } from "@/components/ui/skeleton"
11-
import { Button } from "@/components/ui/button"
14+
import { Button } from "@selftune/ui/primitives"
1215
import { AlertCircleIcon, RefreshCwIcon, RocketIcon, LayersIcon, ActivityIcon, XIcon } from "lucide-react"
1316

1417
function deriveSkillCards(skills: SkillSummary[]): SkillCard[] {
@@ -200,7 +203,11 @@ export function Overview({
200203
/>
201204

202205
<div className="grid grid-cols-1 gap-6 @5xl/main:grid-cols-[1fr_320px]">
203-
<SkillHealthGrid cards={filteredCards} totalCount={cards.length} statusFilter={statusFilter} onStatusFilterChange={onStatusFilterChange} />
206+
<SkillHealthGrid cards={filteredCards} totalCount={cards.length} statusFilter={statusFilter} onStatusFilterChange={onStatusFilterChange} renderSkillName={(skill) => (
207+
<Link to={`/skills/${encodeURIComponent(skill.name)}`} className="text-sm font-medium hover:underline">
208+
{skill.name}
209+
</Link>
210+
)} />
204211

205212
<div className="px-4 lg:px-6 @5xl/main:px-0 @5xl/main:pr-4 lg:@5xl/main:pr-6">
206213
<div className="sticky top-4 space-y-4">

apps/local-dashboard/src/pages/SkillReport.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
import { useEffect, useState } from "react"
22
import { Link, useParams } from "react-router-dom"
3-
import { Badge } from "@/components/ui/badge"
4-
import { Button } from "@/components/ui/button"
53
import {
4+
Badge,
5+
Button,
66
Card,
77
CardAction,
88
CardContent,
99
CardDescription,
1010
CardHeader,
1111
CardTitle,
12-
} from "@/components/ui/card"
13-
import { Skeleton } from "@/components/ui/skeleton"
14-
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
15-
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
16-
import {
1712
Table,
1813
TableBody,
1914
TableCell,
2015
TableHead,
2116
TableHeader,
2217
TableRow,
23-
} from "@/components/ui/table"
24-
import { EvolutionTimeline } from "@/components/EvolutionTimeline"
25-
import { EvidenceViewer } from "@/components/EvidenceViewer"
26-
import { InfoTip } from "@/components/InfoTip"
18+
Tabs,
19+
TabsList,
20+
TabsTrigger,
21+
TabsContent,
22+
Tooltip,
23+
TooltipContent,
24+
TooltipTrigger,
25+
} from "@selftune/ui/primitives"
26+
import { Skeleton } from "@/components/ui/skeleton"
27+
import { EvolutionTimeline } from "@selftune/ui/components"
28+
import { EvidenceViewer } from "@selftune/ui/components"
29+
import { InfoTip } from "@selftune/ui/components"
2730
import { useSkillReport } from "@/hooks/useSkillReport"
28-
import { STATUS_CONFIG } from "@/constants"
29-
import { deriveStatus, formatRate, timeAgo } from "@/utils"
31+
import { STATUS_CONFIG } from "@selftune/ui/lib"
32+
import { deriveStatus, formatRate, timeAgo } from "@selftune/ui/lib"
3033
import {
3134
AlertCircleIcon,
3235
ArrowLeftIcon,

0 commit comments

Comments
 (0)