@openconsole/shadcn 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +460 -380
  2. package/components/ai-elements/agent.tsx +141 -0
  3. package/components/ai-elements/artifact.tsx +148 -0
  4. package/components/ai-elements/attachments.tsx +426 -0
  5. package/components/ai-elements/audio-player.tsx +231 -0
  6. package/components/ai-elements/canvas.tsx +26 -0
  7. package/components/ai-elements/chain-of-thought.tsx +222 -0
  8. package/components/ai-elements/checkpoint.tsx +71 -0
  9. package/components/ai-elements/code-block.tsx +562 -0
  10. package/components/ai-elements/commit.tsx +458 -0
  11. package/components/ai-elements/confirmation.tsx +174 -0
  12. package/components/ai-elements/connection.tsx +28 -0
  13. package/components/ai-elements/context.tsx +409 -0
  14. package/components/ai-elements/controls.tsx +18 -0
  15. package/components/ai-elements/conversation.tsx +168 -0
  16. package/components/ai-elements/edge.tsx +143 -0
  17. package/components/ai-elements/environment-variables.tsx +324 -0
  18. package/components/ai-elements/file-tree.tsx +304 -0
  19. package/components/ai-elements/image.tsx +24 -0
  20. package/components/ai-elements/index.ts +51 -0
  21. package/components/ai-elements/inline-citation.tsx +296 -0
  22. package/components/ai-elements/jsx-preview.tsx +310 -0
  23. package/components/ai-elements/message.tsx +360 -0
  24. package/components/ai-elements/mic-selector.tsx +375 -0
  25. package/components/ai-elements/model-selector.tsx +213 -0
  26. package/components/ai-elements/node.tsx +71 -0
  27. package/components/ai-elements/open-in-chat.tsx +370 -0
  28. package/components/ai-elements/package-info.tsx +239 -0
  29. package/components/ai-elements/panel.tsx +15 -0
  30. package/components/ai-elements/persona.tsx +306 -0
  31. package/components/ai-elements/plan.tsx +147 -0
  32. package/components/ai-elements/prompt-input.tsx +1463 -0
  33. package/components/ai-elements/queue.tsx +274 -0
  34. package/components/ai-elements/reasoning.tsx +228 -0
  35. package/components/ai-elements/sandbox.tsx +132 -0
  36. package/components/ai-elements/schema-display.tsx +471 -0
  37. package/components/ai-elements/shimmer.tsx +77 -0
  38. package/components/ai-elements/snippet.tsx +145 -0
  39. package/components/ai-elements/sources.tsx +77 -0
  40. package/components/ai-elements/speech-input.tsx +323 -0
  41. package/components/ai-elements/stack-trace.tsx +528 -0
  42. package/components/ai-elements/suggestion.tsx +57 -0
  43. package/components/ai-elements/task.tsx +87 -0
  44. package/components/ai-elements/terminal.tsx +273 -0
  45. package/components/ai-elements/test-results.tsx +496 -0
  46. package/components/ai-elements/tool.tsx +173 -0
  47. package/components/ai-elements/toolbar.tsx +16 -0
  48. package/components/ai-elements/transcription.tsx +125 -0
  49. package/components/ai-elements/voice-selector.tsx +524 -0
  50. package/components/ai-elements/web-preview.tsx +281 -0
  51. package/components/index.ts +3 -0
  52. package/{accordion.tsx → components/ui/accordion.tsx} +66 -66
  53. package/{alert-dialog.tsx → components/ui/alert-dialog.tsx} +196 -196
  54. package/{alert.tsx → components/ui/alert.tsx} +66 -66
  55. package/{aspect-ratio.tsx → components/ui/aspect-ratio.tsx} +11 -11
  56. package/{avatar.tsx → components/ui/avatar.tsx} +53 -53
  57. package/{badge.tsx → components/ui/badge.tsx} +46 -46
  58. package/{breadcrumb.tsx → components/ui/breadcrumb.tsx} +109 -109
  59. package/{button-group.tsx → components/ui/button-group.tsx} +83 -83
  60. package/{button.tsx → components/ui/button.tsx} +60 -60
  61. package/{calendar.tsx → components/ui/calendar.tsx} +219 -219
  62. package/{card.tsx → components/ui/card.tsx} +92 -92
  63. package/{carousel.tsx → components/ui/carousel.tsx} +241 -241
  64. package/{chart.tsx → components/ui/chart.tsx} +374 -374
  65. package/{checkbox.tsx → components/ui/checkbox.tsx} +32 -32
  66. package/{collapsible.tsx → components/ui/collapsible.tsx} +33 -33
  67. package/{command.tsx → components/ui/command.tsx} +184 -184
  68. package/{context-menu.tsx → components/ui/context-menu.tsx} +252 -252
  69. package/{dialog.tsx → components/ui/dialog.tsx} +143 -143
  70. package/{direction.tsx → components/ui/direction.tsx} +22 -22
  71. package/{drawer.tsx → components/ui/drawer.tsx} +135 -135
  72. package/{dropdown-menu.tsx → components/ui/dropdown-menu.tsx} +257 -257
  73. package/{empty.tsx → components/ui/empty.tsx} +104 -104
  74. package/{field.tsx → components/ui/field.tsx} +248 -248
  75. package/{form.tsx → components/ui/form.tsx} +167 -167
  76. package/{hover-card.tsx → components/ui/hover-card.tsx} +44 -44
  77. package/{icon.tsx → components/ui/icon.tsx} +55 -55
  78. package/components/ui/index.ts +59 -0
  79. package/{input-group.tsx → components/ui/input-group.tsx} +170 -170
  80. package/{input-otp.tsx → components/ui/input-otp.tsx} +77 -77
  81. package/{input.tsx → components/ui/input.tsx} +21 -21
  82. package/{item.tsx → components/ui/item.tsx} +193 -193
  83. package/{kbd.tsx → components/ui/kbd.tsx} +28 -28
  84. package/{label.tsx → components/ui/label.tsx} +24 -24
  85. package/{menubar.tsx → components/ui/menubar.tsx} +276 -276
  86. package/{native-select.tsx → components/ui/native-select.tsx} +62 -62
  87. package/{navigation-menu.tsx → components/ui/navigation-menu.tsx} +168 -168
  88. package/{pagination.tsx → components/ui/pagination.tsx} +127 -127
  89. package/{popover.tsx → components/ui/popover.tsx} +89 -89
  90. package/{progress.tsx → components/ui/progress.tsx} +31 -31
  91. package/{radio-group.tsx → components/ui/radio-group.tsx} +45 -45
  92. package/{resizable.tsx → components/ui/resizable.tsx} +53 -53
  93. package/{scroll-area.tsx → components/ui/scroll-area.tsx} +58 -58
  94. package/{select.tsx → components/ui/select.tsx} +187 -187
  95. package/{separator.tsx → components/ui/separator.tsx} +28 -28
  96. package/{sheet.tsx → components/ui/sheet.tsx} +139 -139
  97. package/{sidebar.tsx → components/ui/sidebar.tsx} +724 -724
  98. package/{skeleton.tsx → components/ui/skeleton.tsx} +13 -13
  99. package/{slider.tsx → components/ui/slider.tsx} +63 -63
  100. package/{sonner.tsx → components/ui/sonner.tsx} +40 -40
  101. package/{spinner.tsx → components/ui/spinner.tsx} +16 -16
  102. package/{switch.tsx → components/ui/switch.tsx} +35 -35
  103. package/{table.tsx → components/ui/table.tsx} +116 -116
  104. package/{tabs.tsx → components/ui/tabs.tsx} +66 -66
  105. package/{textarea.tsx → components/ui/textarea.tsx} +18 -18
  106. package/{toggle-group.tsx → components/ui/toggle-group.tsx} +83 -83
  107. package/{toggle.tsx → components/ui/toggle.tsx} +47 -47
  108. package/{tooltip.tsx → components/ui/tooltip.tsx} +61 -61
  109. package/hooks/index.ts +1 -1
  110. package/hooks/use-mobile.ts +19 -19
  111. package/index.ts +3 -59
  112. package/lib/index.ts +1 -1
  113. package/lib/utils.ts +6 -6
  114. package/package.json +79 -1
  115. package/styles.css +124 -124
  116. package/tsconfig.json +0 -12
  117. package/tsconfig.tsbuildinfo +0 -1
package/README.md CHANGED
@@ -1,380 +1,460 @@
1
- # @openconsole/shadcn
2
-
3
- > 整套 [shadcn/ui](https://ui.shadcn.com) 原语 + 设计 token,按包发布,配套 Tailwind v4 主题与 OKLCH 配色。
4
-
5
- `@openconsole/shadcn` 把 shadcn 官方组件直接落进 monorepo 作为可发布的内部包,相比 CLI 拷贝到每个应用的做法:
6
-
7
- - 升级集中:所有应用共用同一份组件实现。
8
- - **平铺命名空间**:所有组件都从 `@openconsole/shadcn` 一行导入,不需要按文件路径区分。
9
- - **样式开箱即用**:`@openconsole/shadcn/styles.css` 自带 Tailwind v4 `@theme` 配置与默认明暗变量。
10
- - **附赠**:`cn()` 工具、`<Icon>` 字符串图标渲染、`useIsMobile()`、`<DirectionProvider>`。
11
- - 上层包 `@openconsole/atoms` 在这一层之上做更高层次的组合(Header / Sidebar / Preferences)。
12
-
13
- ## 安装
14
-
15
- ```bash
16
- pnpm add @openconsole/shadcn
17
- ```
18
-
19
- monorepo 内部包消费时需要在 `next.config.ts` 加入 `transpilePackages`,否则 Next.js 不会解析 `.tsx` 源码:
20
-
21
- ```ts
22
- const nextConfig = {
23
- transpilePackages: ["@openconsole/shadcn"],
24
- };
25
- ```
26
-
27
- 引入样式(**在所有应用样式之前**):
28
-
29
- ```ts
30
- // app/layout.tsx
31
- import "@openconsole/shadcn/styles.css";
32
- ```
33
-
34
- 确保你的 Tailwind 配置/PostCSS 链路工作(Tailwind v4 是 zero-config,绝大多数情况下只要 import 样式即可)。
35
-
36
- > **Peer 依赖**:见 [`package.json`](./package.json) `peerDependencies` 字段。Radix UI、`lucide-react`、`class-variance-authority`、`clsx`、`tailwind-merge`、`tailwindcss@^4` 等都必须由应用层提供。
37
-
38
- ## 用法
39
-
40
- ```tsx
41
- import { Button, Input, Card, CardHeader, CardTitle, CardContent } from "@openconsole/shadcn";
42
-
43
- export default function Demo() {
44
- return (
45
- <Card>
46
- <CardHeader><CardTitle>登录</CardTitle></CardHeader>
47
- <CardContent className="space-y-4">
48
- <Input placeholder="邮箱" />
49
- <Button className="w-full">提交</Button>
50
- </CardContent>
51
- </Card>
52
- );
53
- }
54
- ```
55
-
56
- 所有组件都是平铺导出 —— **不要**深路径 import:
57
-
58
- ```ts
59
- // ✅
60
- import { Button } from "@openconsole/shadcn";
61
-
62
- // ❌
63
- import { Button } from "@openconsole/shadcn/button";
64
- ```
65
-
66
- ## 组件总览
67
-
68
- 按 shadcn 的语义分类组织。每个组件在 [shadcn/ui 官网](https://ui.shadcn.com/docs/components) 都能找到详尽文档;本包是它的镜像版本,**API 与 props 与官方一致**。
69
-
70
- ### 布局 & 容器
71
-
72
- | 组件 | 用途 |
73
- | ---------------------------------------------- | ------------------------------- |
74
- | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, `CardAction` | 卡片容器 |
75
- | `Separator` | 水平/垂直分割线 |
76
- | `AspectRatio` | 固定宽高比容器 |
77
- | `ScrollArea`, `ScrollBar` | 自定义滚动条 |
78
- | `Resizable`, `ResizablePanel`, `ResizableHandle`, `ResizablePanelGroup` | 可拖拽分屏 |
79
- | `Sidebar`, `SidebarProvider`, `SidebarInset`, `SidebarTrigger`, `SidebarRail`, `SidebarHeader`, `SidebarFooter`, `SidebarContent`, `SidebarGroup`, `SidebarMenu`, … + `useSidebar` | 完整侧边栏体系 |
80
-
81
- ### 按钮 & 行动元素
82
-
83
- | 组件 | 用途 |
84
- | ------------------------------------------ | -------------------------------------- |
85
- | `Button` + `buttonVariants` | 主按钮组件 + cva 变体 |
86
- | `ButtonGroup` | 按钮组合 |
87
- | `Toggle`, `toggleVariants` | 单击切换按钮 |
88
- | `ToggleGroup`, `ToggleGroupItem` | 多选/单选切换按钮组 |
89
-
90
- ### 表单输入
91
-
92
- | 组件 | 用途 |
93
- | --------------------------------------------------------------------------------- | ----------------------------- |
94
- | `Input` | 文本输入 |
95
- | `Textarea` | 多行文本输入 |
96
- | `Label` | 表单标签 |
97
- | `Checkbox` | 复选框 |
98
- | `RadioGroup`, `RadioGroupItem` | 单选框组 |
99
- | `Switch` | 开关 |
100
- | `Slider` | 拖动条 |
101
- | `Select`, `SelectContent`, `SelectItem`, `SelectTrigger`, `SelectValue`, ... | Radix Select |
102
- | `NativeSelect`, `NativeSelectContent`, ... | 原生 `<select>` 包装 |
103
- | `InputOTP`, `InputOTPGroup`, `InputOTPSlot`, `InputOTPSeparator` | OTP 验证码输入 |
104
- | `Field`, `FieldLabel`, `FieldDescription`, `FieldError`, ... | 表单字段包装 |
105
- | `InputGroup`, `InputGroupInput`, `InputGroupAddon`, ... | 输入组(带前后缀) |
106
- | `Form`, `FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormDescription`, `FormMessage`, `useFormField` | react-hook-form 集成 |
107
-
108
- ### 反馈
109
-
110
- | 组件 | 用途 |
111
- | ------------------------------------------------------------------------------------ | --------------------- |
112
- | `Alert`, `AlertTitle`, `AlertDescription` | 静态提示横幅 |
113
- | `AlertDialog`, `AlertDialogTrigger`, `AlertDialogContent`, `AlertDialogAction`, ... | 确认对话框 |
114
- | `Toaster` | sonner Toast 容器 |
115
- | `Progress` | 进度条 |
116
- | `Spinner` | 加载圈 |
117
- | `Skeleton` | 骨架屏占位 |
118
- | `Empty`, `EmptyHeader`, `EmptyTitle`, `EmptyDescription`, `EmptyContent`, ... | 空状态 |
119
-
120
- ### 浮层 & 弹窗
121
-
122
- | 组件 | 用途 |
123
- | --------------------------------------------------------------------------------- | ----------------- |
124
- | `Dialog`, `DialogTrigger`, `DialogContent`, ... | 模态对话框 |
125
- | `Drawer`, `DrawerTrigger`, `DrawerContent`, ... | vaul 抽屉 |
126
- | `Sheet`, `SheetTrigger`, `SheetContent`, `SheetHeader`, `SheetTitle`, ... | 侧滑抽屉 |
127
- | `Popover`, `PopoverTrigger`, `PopoverContent` | 气泡卡片 |
128
- | `HoverCard`, `HoverCardTrigger`, `HoverCardContent` | 悬停卡片 |
129
- | `Tooltip`, `TooltipProvider`, `TooltipTrigger`, `TooltipContent` | 工具提示 |
130
-
131
- ### 菜单
132
-
133
- | 组件 | 用途 |
134
- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------- |
135
- | `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, ... | 下拉菜单 |
136
- | `ContextMenu`, `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`, ... | 右键菜单 |
137
- | `Menubar`, `MenubarMenu`, `MenubarTrigger`, `MenubarContent`, `MenubarItem`, ... | 桌面应用风格菜单栏 |
138
- | `NavigationMenu`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, ... | 多级导航菜单 |
139
- | `Command`, `CommandDialog`, `CommandInput`, `CommandList`, `CommandGroup`, `CommandItem`, `CommandShortcut`, ... | cmdk 命令面板 |
140
-
141
- ### 数据展示
142
-
143
- | 组件 | 用途 |
144
- | --------------------------------------------------------------------------------- | --------------------- |
145
- | `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableHead`, `TableRow`, `TableCell`, `TableCaption` | 表格 |
146
- | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink`, `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis` | 分页器 |
147
- | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` | 标签页 |
148
- | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` | 手风琴 |
149
- | `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | 折叠面板 |
150
- | `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `useCarousel` | embla 轮播 |
151
- | `Chart`, `ChartContainer`, `ChartTooltip`, `ChartTooltipContent`, `ChartLegend`, ...| recharts 主题集成 |
152
- | `Item`, `ItemGroup`, `ItemHeader`, `ItemTitle`, `ItemDescription`, `ItemContent`, ...| 通用列表项卡片 |
153
-
154
- ### 工具元素
155
-
156
- | 组件 | 用途 |
157
- | ---------------------------------------------------------- | ------------------------------- |
158
- | `Avatar`, `AvatarImage`, `AvatarFallback` | 头像 |
159
- | `Badge` | 徽章 |
160
- | `Kbd`, `KbdGroup` | 键盘按键文本 |
161
- | `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, `BreadcrumbSeparator`, `BreadcrumbEllipsis` | 面包屑 |
162
- | `Calendar`, `CalendarDayButton` | react-day-picker 日历 |
163
-
164
- ### 工具与公共导出
165
-
166
- | 名称 | 用途 |
167
- | --------------------- | --------------------------------------------------------------------------------- |
168
- | `cn(...inputs)` | `clsx + tailwind-merge`,合并 class 并消除 Tailwind 冲突 |
169
- | `Icon` | 按名称字符串渲染 `lucide-react` 图标,便于在 RSC ↔ Client 间传递可序列化数据 |
170
- | `useIsMobile()` | 监听 `(max-width: 767px)`,返回当前是否为移动端宽度 |
171
- | `DirectionProvider`, `useDirection` | Radix Direction(LTR / RTL),影响所有 Radix 子项的方向感知 |
172
-
173
- ## 示例
174
-
175
- ### `cn()` 合并 class
176
-
177
- ```ts
178
- import { cn } from "@openconsole/shadcn";
179
-
180
- <button
181
- className={cn(
182
- "rounded-md px-3 py-1",
183
- isPrimary && "bg-primary text-primary-foreground",
184
- className, // 调用方覆盖
185
- )}
186
- />
187
- ```
188
-
189
- `twMerge` 自动消除 Tailwind 冲突(例如 `px-2` 与 `px-4` 写一起会保留后写的)。
190
-
191
- ### `<Icon>` 字符串图标
192
-
193
- Sider / 菜单等数据结构里把图标存成字符串可以让 Server Component 安全传给 Client Component。`<Icon>` 在 client 端查表渲染:
194
-
195
- ```tsx
196
- import { Icon } from "@openconsole/shadcn";
197
-
198
- const menu = [
199
- { label: "概览", icon: "LayoutDashboard", href: "/dashboard" },
200
- { label: "订单", icon: "ShoppingCart", href: "/orders" },
201
- ];
202
-
203
- <Icon name={menu[0].icon} className="size-4" />
204
- ```
205
-
206
- `name` 不存在时返回 `null`,不会抛错。
207
-
208
- ### `useIsMobile()` 响应式分支
209
-
210
- ```tsx
211
- import { useIsMobile } from "@openconsole/shadcn";
212
-
213
- function Page() {
214
- const isMobile = useIsMobile();
215
- return isMobile ? <MobileView /> : <DesktopView />;
216
- }
217
- ```
218
-
219
- 第一次 mount 前返回 `false`(SSR 安全)。
220
-
221
- ### `<DirectionProvider>` RTL
222
-
223
- ```tsx
224
- import { DirectionProvider } from "@openconsole/shadcn";
225
-
226
- <DirectionProvider direction="rtl">
227
- {/* 所有 Radix 子项自动按右到左渲染 */}
228
- </DirectionProvider>
229
- ```
230
-
231
- ### `<Sidebar>` 完整骨架
232
-
233
- shadcn sidebar 体系自包含。如果你只用到底层原语:
234
-
235
- ```tsx
236
- import {
237
- SidebarProvider,
238
- Sidebar,
239
- SidebarHeader,
240
- SidebarContent,
241
- SidebarFooter,
242
- SidebarRail,
243
- SidebarInset,
244
- } from "@openconsole/shadcn";
245
-
246
- <SidebarProvider>
247
- <Sidebar>
248
- <SidebarHeader>{/* 品牌 */}</SidebarHeader>
249
- <SidebarContent>{/* 菜单 */}</SidebarContent>
250
- <SidebarFooter>{/* 账号 */}</SidebarFooter>
251
- <SidebarRail />
252
- </Sidebar>
253
- <SidebarInset>
254
- {children}
255
- </SidebarInset>
256
- </SidebarProvider>
257
- ```
258
-
259
- 要更高层的封装,请直接使用 `@openconsole/atoms` 的 `<Sidebar>`。
260
-
261
- ### Form + react-hook-form + zod
262
-
263
- ```tsx
264
- import { useForm } from "react-hook-form";
265
- import { zodResolver } from "@hookform/resolvers/zod";
266
- import { z } from "zod";
267
- import {
268
- Button,
269
- Form,
270
- FormControl,
271
- FormField,
272
- FormItem,
273
- FormLabel,
274
- FormMessage,
275
- Input,
276
- } from "@openconsole/shadcn";
277
-
278
- const schema = z.object({ email: z.string().email() });
279
-
280
- function SignIn() {
281
- const form = useForm({ resolver: zodResolver(schema) });
282
- return (
283
- <Form {...form}>
284
- <form onSubmit={form.handleSubmit((v) => signIn(v))}>
285
- <FormField
286
- control={form.control}
287
- name="email"
288
- render={({ field }) => (
289
- <FormItem>
290
- <FormLabel>邮箱</FormLabel>
291
- <FormControl><Input {...field} /></FormControl>
292
- <FormMessage />
293
- </FormItem>
294
- )}
295
- />
296
- <Button type="submit">登录</Button>
297
- </form>
298
- </Form>
299
- );
300
- }
301
- ```
302
-
303
- ## 主题与 Token
304
-
305
- `@openconsole/shadcn/styles.css` 包含三部分:
306
-
307
- 1. `@theme inline { … }` —— 把 CSS 变量映射成 Tailwind v4 设计 token(`bg-primary` ↔ `var(--primary)`)。
308
- 2. `:root { … }` —— light 模式的默认 OKLCH 配色。
309
- 3. `.dark { … }` —— dark 模式的默认 OKLCH 配色。
310
-
311
- ### 自定义主题
312
-
313
- 直接覆盖 CSS 变量即可:
314
-
315
- ```css
316
- :root {
317
- --primary: oklch(0.6 0.2 250);
318
- --primary-foreground: oklch(0.98 0 0);
319
- --radius: 0.5rem;
320
- }
321
- ```
322
-
323
- 或者使用 `@openconsole/atoms` 的 `<Preferences>` 抽屉做运行时实时编辑(包含 shadcn / tweakcn 数百套预设、品牌色按 token 编辑、CSS 粘贴导入)。
324
-
325
- ### `next-themes` 接入
326
-
327
- 通常配合 `<ThemeProvider attribute="class">` 切换 `.dark` 类名。直接用本包:
328
-
329
- ```tsx
330
- import { ThemeProvider } from "next-themes";
331
-
332
- <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
333
- {children}
334
- </ThemeProvider>
335
- ```
336
-
337
- 或使用 `@openconsole/atoms` 的 `<ThemeProvider>`(带默认值的薄包装)。
338
-
339
- ## 与 shadcn/ui CLI 的差异
340
-
341
- | 维度 | `@openconsole/shadcn` | `npx shadcn add` |
342
- | ---------- | ------------------------------------ | ----------------------------- |
343
- | 分发方式 | 包,集中升级 | 拷贝到应用,分散维护 |
344
- | 导入方式 | 平铺 `from "@openconsole/shadcn"` | 按文件路径 `from "@/components/ui/button"` |
345
- | 样式 | `import "@openconsole/shadcn/styles.css"` 一行就位 | 拷贝 `globals.css` 后手工维护 |
346
- | 升级 | 升级包 | 重新 add + 手工 diff |
347
- | 跨应用复用 | 一份代码 N 个应用 | N 份相同代码 |
348
- | 修改组件 | 改源码即可,monorepo 内立刻生效 | 自由度更高 |
349
-
350
- ## 常见问题
351
-
352
- **Q:为什么导入路径里没有 `/button` / `/card`?**
353
- A:本包用 `index.ts` 平铺所有导出,强制让所有组件走 `@openconsole/shadcn`。这样升级只改一处 `import`。深路径不受支持,未来重构组件文件结构时不会破坏调用方。
354
-
355
- **Q:我能像 shadcn CLI 那样直接改组件源码吗?**
356
- A:能。monorepo `packages/shadcn/<component>.tsx` 是直接源码,按 `transpilePackages` 配置编译到应用。改完立刻看到效果。但请注意改动会影响所有依赖该组件的应用。
357
-
358
- **Q:为什么需要传给 `<Icon>` 字符串而不是直接 React 组件?**
359
- A:菜单 / 导航这类数据结构会被 RSC ↔ Client 间传递。React 组件不可序列化,字符串可以。把图标查表延迟到 Client 端做。
360
-
361
- **Q:`useIsMobile()` 的断点能改吗?**
362
- A:当前断点是 `768px`,硬编码在 [`hooks/use-mobile.ts`](./hooks/use-mobile.ts)。需要不同断点就直接用 `window.matchMedia` 自己写一个。
363
-
364
- **Q:Tailwind v4 配置去哪了?**
365
- A:v4 是 zero-config。本包通过 `styles.css` 里的 `@source "./**/*.{ts,tsx}"` `@theme inline { }` 完成等价的「配置」。应用层无需再写 `tailwind.config.js`。
366
-
367
- ## 与 `@openconsole/atoms` 的关系
368
-
369
- `@openconsole/atoms` 在本包之上做了更高层的组合:
370
-
371
- | 层次 | 包 | 关注点 |
372
- | ------------------------ | ------------------------ | ------------------------------------- |
373
- | 设计 token + 原语 | `@openconsole/shadcn` | 按钮 / 输入 / 弹窗 / 分页… |
374
- | 业务级骨架 | `@openconsole/atoms` | Header / Sidebar / Breadcrumbs / 错误页 / Preferences |
375
-
376
- 只用得到原语:直接 `@openconsole/shadcn`。要做后台骨架:装 `@openconsole/atoms`(它已经把 shadcn 列为 peer 依赖)。
377
-
378
- ## License
379
-
380
- 参见仓库根目录的 LICENSE。
1
+ # @openconsole/shadcn
2
+
3
+ > 整套 [shadcn/ui](https://ui.shadcn.com) 原语 + 设计 token,按包发布,配套 Tailwind v4 主题与 OKLCH 配色。
4
+
5
+ `@openconsole/shadcn` 把 shadcn 官方组件直接落进 monorepo 作为可发布的内部包,相比 CLI 拷贝到每个应用的做法:
6
+
7
+ - 升级集中:所有应用共用同一份组件实现。
8
+ - **平铺命名空间**:所有组件都从 `@openconsole/shadcn` 一行导入,不需要按文件路径区分。
9
+ - **样式开箱即用**:`@openconsole/shadcn/styles.css` 自带 Tailwind v4 `@theme` 配置与默认明暗变量。
10
+ - **附赠**:`cn()` 工具、`<Icon>` 字符串图标渲染、`useIsMobile()`、`<DirectionProvider>`。
11
+ - 上层包 `@openconsole/atoms` 在这一层之上做更高层次的组合(Header / Sidebar / Preferences)。
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ pnpm add @openconsole/shadcn
17
+ ```
18
+
19
+ monorepo 内部包消费时需要在 `next.config.ts` 加入 `transpilePackages`,否则 Next.js 不会解析 `.tsx` 源码:
20
+
21
+ ```ts
22
+ const nextConfig = {
23
+ transpilePackages: ["@openconsole/shadcn"],
24
+ };
25
+ ```
26
+
27
+ 引入样式(**在所有应用样式之前**):
28
+
29
+ ```ts
30
+ // app/layout.tsx
31
+ import "@openconsole/shadcn/styles.css";
32
+ ```
33
+
34
+ 确保你的 Tailwind 配置/PostCSS 链路工作(Tailwind v4 是 zero-config,绝大多数情况下只要 import 样式即可)。
35
+
36
+ > **Peer 依赖**:见 [`package.json`](./package.json) `peerDependencies` 字段。Radix UI、`lucide-react`、`class-variance-authority`、`clsx`、`tailwind-merge`、`tailwindcss@^4` 等都必须由应用层提供。
37
+
38
+ ## 用法
39
+
40
+ ```tsx
41
+ import { Button, Input, Card, CardHeader, CardTitle, CardContent } from "@openconsole/shadcn";
42
+
43
+ export default function Demo() {
44
+ return (
45
+ <Card>
46
+ <CardHeader><CardTitle>登录</CardTitle></CardHeader>
47
+ <CardContent className="space-y-4">
48
+ <Input placeholder="邮箱" />
49
+ <Button className="w-full">提交</Button>
50
+ </CardContent>
51
+ </Card>
52
+ );
53
+ }
54
+ ```
55
+
56
+ 所有组件都是平铺导出 —— **不要**深路径 import:
57
+
58
+ ```ts
59
+ // ✅
60
+ import { Button } from "@openconsole/shadcn";
61
+
62
+ // ❌
63
+ import { Button } from "@openconsole/shadcn/button";
64
+ ```
65
+
66
+ ## 组件总览
67
+
68
+ 按 shadcn 的语义分类组织。每个组件在 [shadcn/ui 官网](https://ui.shadcn.com/docs/components) 都能找到详尽文档;本包是它的镜像版本,**API 与 props 与官方一致**。
69
+
70
+ > 下列为 shadcn 原语;AI 聊天 / 工作流类组件(Conversation、Message、PromptInput …)另见 [AI SDK Elements](#ai-sdk-elements)。
71
+
72
+ ### 布局 & 容器
73
+
74
+ | 组件 | 用途 |
75
+ | ---------------------------------------------- | ------------------------------- |
76
+ | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, `CardAction` | 卡片容器 |
77
+ | `Separator` | 水平/垂直分割线 |
78
+ | `AspectRatio` | 固定宽高比容器 |
79
+ | `ScrollArea`, `ScrollBar` | 自定义滚动条 |
80
+ | `Resizable`, `ResizablePanel`, `ResizableHandle`, `ResizablePanelGroup` | 可拖拽分屏 |
81
+ | `Sidebar`, `SidebarProvider`, `SidebarInset`, `SidebarTrigger`, `SidebarRail`, `SidebarHeader`, `SidebarFooter`, `SidebarContent`, `SidebarGroup`, `SidebarMenu`, … + `useSidebar` | 完整侧边栏体系 |
82
+
83
+ ### 按钮 & 行动元素
84
+
85
+ | 组件 | 用途 |
86
+ | ------------------------------------------ | -------------------------------------- |
87
+ | `Button` + `buttonVariants` | 主按钮组件 + cva 变体 |
88
+ | `ButtonGroup` | 按钮组合 |
89
+ | `Toggle`, `toggleVariants` | 单击切换按钮 |
90
+ | `ToggleGroup`, `ToggleGroupItem` | 多选/单选切换按钮组 |
91
+
92
+ ### 表单输入
93
+
94
+ | 组件 | 用途 |
95
+ | --------------------------------------------------------------------------------- | ----------------------------- |
96
+ | `Input` | 文本输入 |
97
+ | `Textarea` | 多行文本输入 |
98
+ | `Label` | 表单标签 |
99
+ | `Checkbox` | 复选框 |
100
+ | `RadioGroup`, `RadioGroupItem` | 单选框组 |
101
+ | `Switch` | 开关 |
102
+ | `Slider` | 拖动条 |
103
+ | `Select`, `SelectContent`, `SelectItem`, `SelectTrigger`, `SelectValue`, ... | Radix Select |
104
+ | `NativeSelect`, `NativeSelectContent`, ... | 原生 `<select>` 包装 |
105
+ | `InputOTP`, `InputOTPGroup`, `InputOTPSlot`, `InputOTPSeparator` | OTP 验证码输入 |
106
+ | `Field`, `FieldLabel`, `FieldDescription`, `FieldError`, ... | 表单字段包装 |
107
+ | `InputGroup`, `InputGroupInput`, `InputGroupAddon`, ... | 输入组(带前后缀) |
108
+ | `Form`, `FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormDescription`, `FormMessage`, `useFormField` | react-hook-form 集成 |
109
+
110
+ ### 反馈
111
+
112
+ | 组件 | 用途 |
113
+ | ------------------------------------------------------------------------------------ | --------------------- |
114
+ | `Alert`, `AlertTitle`, `AlertDescription` | 静态提示横幅 |
115
+ | `AlertDialog`, `AlertDialogTrigger`, `AlertDialogContent`, `AlertDialogAction`, ... | 确认对话框 |
116
+ | `Toaster` | sonner Toast 容器 |
117
+ | `Progress` | 进度条 |
118
+ | `Spinner` | 加载圈 |
119
+ | `Skeleton` | 骨架屏占位 |
120
+ | `Empty`, `EmptyHeader`, `EmptyTitle`, `EmptyDescription`, `EmptyContent`, ... | 空状态 |
121
+
122
+ ### 浮层 & 弹窗
123
+
124
+ | 组件 | 用途 |
125
+ | --------------------------------------------------------------------------------- | ----------------- |
126
+ | `Dialog`, `DialogTrigger`, `DialogContent`, ... | 模态对话框 |
127
+ | `Drawer`, `DrawerTrigger`, `DrawerContent`, ... | vaul 抽屉 |
128
+ | `Sheet`, `SheetTrigger`, `SheetContent`, `SheetHeader`, `SheetTitle`, ... | 侧滑抽屉 |
129
+ | `Popover`, `PopoverTrigger`, `PopoverContent` | 气泡卡片 |
130
+ | `HoverCard`, `HoverCardTrigger`, `HoverCardContent` | 悬停卡片 |
131
+ | `Tooltip`, `TooltipProvider`, `TooltipTrigger`, `TooltipContent` | 工具提示 |
132
+
133
+ ### 菜单
134
+
135
+ | 组件 | 用途 |
136
+ | ----------------------------------------------------------------------------------------------------------------------------- | ------------------- |
137
+ | `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, ... | 下拉菜单 |
138
+ | `ContextMenu`, `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`, ... | 右键菜单 |
139
+ | `Menubar`, `MenubarMenu`, `MenubarTrigger`, `MenubarContent`, `MenubarItem`, ... | 桌面应用风格菜单栏 |
140
+ | `NavigationMenu`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, ... | 多级导航菜单 |
141
+ | `Command`, `CommandDialog`, `CommandInput`, `CommandList`, `CommandGroup`, `CommandItem`, `CommandShortcut`, ... | cmdk 命令面板 |
142
+
143
+ ### 数据展示
144
+
145
+ | 组件 | 用途 |
146
+ | --------------------------------------------------------------------------------- | --------------------- |
147
+ | `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableHead`, `TableRow`, `TableCell`, `TableCaption` | 表格 |
148
+ | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink`, `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis` | 分页器 |
149
+ | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` | 标签页 |
150
+ | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` | 手风琴 |
151
+ | `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | 折叠面板 |
152
+ | `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `useCarousel` | embla 轮播 |
153
+ | `Chart`, `ChartContainer`, `ChartTooltip`, `ChartTooltipContent`, `ChartLegend`, ...| recharts 主题集成 |
154
+ | `Item`, `ItemGroup`, `ItemHeader`, `ItemTitle`, `ItemDescription`, `ItemContent`, ...| 通用列表项卡片 |
155
+
156
+ ### 工具元素
157
+
158
+ | 组件 | 用途 |
159
+ | ---------------------------------------------------------- | ------------------------------- |
160
+ | `Avatar`, `AvatarImage`, `AvatarFallback` | 头像 |
161
+ | `Badge` | 徽章 |
162
+ | `Kbd`, `KbdGroup` | 键盘按键文本 |
163
+ | `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, `BreadcrumbSeparator`, `BreadcrumbEllipsis` | 面包屑 |
164
+ | `Calendar`, `CalendarDayButton` | react-day-picker 日历 |
165
+
166
+ ### 工具与公共导出
167
+
168
+ | 名称 | 用途 |
169
+ | --------------------- | --------------------------------------------------------------------------------- |
170
+ | `cn(...inputs)` | `clsx + tailwind-merge`,合并 class 并消除 Tailwind 冲突 |
171
+ | `Icon` | 按名称字符串渲染 `lucide-react` 图标,便于在 RSC Client 间传递可序列化数据 |
172
+ | `useIsMobile()` | 监听 `(max-width: 767px)`,返回当前是否为移动端宽度 |
173
+ | `DirectionProvider`, `useDirection` | Radix Direction(LTR / RTL),影响所有 Radix 子项的方向感知 |
174
+
175
+ ## 示例
176
+
177
+ ### `cn()` 合并 class
178
+
179
+ ```ts
180
+ import { cn } from "@openconsole/shadcn";
181
+
182
+ <button
183
+ className={cn(
184
+ "rounded-md px-3 py-1",
185
+ isPrimary && "bg-primary text-primary-foreground",
186
+ className, // 调用方覆盖
187
+ )}
188
+ />
189
+ ```
190
+
191
+ `twMerge` 自动消除 Tailwind 冲突(例如 `px-2` 与 `px-4` 写一起会保留后写的)。
192
+
193
+ ### `<Icon>` 字符串图标
194
+
195
+ Sider / 菜单等数据结构里把图标存成字符串可以让 Server Component 安全传给 Client Component。`<Icon>` 在 client 端查表渲染:
196
+
197
+ ```tsx
198
+ import { Icon } from "@openconsole/shadcn";
199
+
200
+ const menu = [
201
+ { label: "概览", icon: "LayoutDashboard", href: "/dashboard" },
202
+ { label: "订单", icon: "ShoppingCart", href: "/orders" },
203
+ ];
204
+
205
+ <Icon name={menu[0].icon} className="size-4" />
206
+ ```
207
+
208
+ `name` 不存在时返回 `null`,不会抛错。
209
+
210
+ ### `useIsMobile()` 响应式分支
211
+
212
+ ```tsx
213
+ import { useIsMobile } from "@openconsole/shadcn";
214
+
215
+ function Page() {
216
+ const isMobile = useIsMobile();
217
+ return isMobile ? <MobileView /> : <DesktopView />;
218
+ }
219
+ ```
220
+
221
+ 第一次 mount 前返回 `false`(SSR 安全)。
222
+
223
+ ### `<DirectionProvider>` RTL
224
+
225
+ ```tsx
226
+ import { DirectionProvider } from "@openconsole/shadcn";
227
+
228
+ <DirectionProvider direction="rtl">
229
+ {/* 所有 Radix 子项自动按右到左渲染 */}
230
+ </DirectionProvider>
231
+ ```
232
+
233
+ ### `<Sidebar>` 完整骨架
234
+
235
+ shadcn 的 sidebar 体系自包含。如果你只用到底层原语:
236
+
237
+ ```tsx
238
+ import {
239
+ SidebarProvider,
240
+ Sidebar,
241
+ SidebarHeader,
242
+ SidebarContent,
243
+ SidebarFooter,
244
+ SidebarRail,
245
+ SidebarInset,
246
+ } from "@openconsole/shadcn";
247
+
248
+ <SidebarProvider>
249
+ <Sidebar>
250
+ <SidebarHeader>{/* 品牌 */}</SidebarHeader>
251
+ <SidebarContent>{/* 菜单 */}</SidebarContent>
252
+ <SidebarFooter>{/* 账号 */}</SidebarFooter>
253
+ <SidebarRail />
254
+ </Sidebar>
255
+ <SidebarInset>
256
+ {children}
257
+ </SidebarInset>
258
+ </SidebarProvider>
259
+ ```
260
+
261
+ 要更高层的封装,请直接使用 `@openconsole/atoms` `<Sidebar>`。
262
+
263
+ ### Form + react-hook-form + zod
264
+
265
+ ```tsx
266
+ import { useForm } from "react-hook-form";
267
+ import { zodResolver } from "@hookform/resolvers/zod";
268
+ import { z } from "zod";
269
+ import {
270
+ Button,
271
+ Form,
272
+ FormControl,
273
+ FormField,
274
+ FormItem,
275
+ FormLabel,
276
+ FormMessage,
277
+ Input,
278
+ } from "@openconsole/shadcn";
279
+
280
+ const schema = z.object({ email: z.string().email() });
281
+
282
+ function SignIn() {
283
+ const form = useForm({ resolver: zodResolver(schema) });
284
+ return (
285
+ <Form {...form}>
286
+ <form onSubmit={form.handleSubmit((v) => signIn(v))}>
287
+ <FormField
288
+ control={form.control}
289
+ name="email"
290
+ render={({ field }) => (
291
+ <FormItem>
292
+ <FormLabel>邮箱</FormLabel>
293
+ <FormControl><Input {...field} /></FormControl>
294
+ <FormMessage />
295
+ </FormItem>
296
+ )}
297
+ />
298
+ <Button type="submit">登录</Button>
299
+ </form>
300
+ </Form>
301
+ );
302
+ }
303
+ ```
304
+
305
+ ## 主题与 Token
306
+
307
+ `@openconsole/shadcn/styles.css` 包含三部分:
308
+
309
+ 1. `@theme inline { … }` —— CSS 变量映射成 Tailwind v4 设计 token(`bg-primary` ↔ `var(--primary)`)。
310
+ 2. `:root { … }` —— light 模式的默认 OKLCH 配色。
311
+ 3. `.dark { … }` —— dark 模式的默认 OKLCH 配色。
312
+
313
+ ### 自定义主题
314
+
315
+ 直接覆盖 CSS 变量即可:
316
+
317
+ ```css
318
+ :root {
319
+ --primary: oklch(0.6 0.2 250);
320
+ --primary-foreground: oklch(0.98 0 0);
321
+ --radius: 0.5rem;
322
+ }
323
+ ```
324
+
325
+ 或者使用 `@openconsole/atoms` 的 `<Preferences>` 抽屉做运行时实时编辑(包含 shadcn / tweakcn 数百套预设、品牌色按 token 编辑、CSS 粘贴导入)。
326
+
327
+ ### `next-themes` 接入
328
+
329
+ 通常配合 `<ThemeProvider attribute="class">` 切换 `.dark` 类名。直接用本包:
330
+
331
+ ```tsx
332
+ import { ThemeProvider } from "next-themes";
333
+
334
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
335
+ {children}
336
+ </ThemeProvider>
337
+ ```
338
+
339
+ 或使用 `@openconsole/atoms` `<ThemeProvider>`(带默认值的薄包装)。
340
+
341
+ ## 目录结构与维护
342
+
343
+ 本包采用 **shadcn 标准工程布局**,让官方 CLI(`add` / `diff`)与 AI Elements registry 原生可用:
344
+
345
+ ```
346
+ packages/shadcn/
347
+ ├─ components/
348
+ │ ├─ ui/<name>.tsx # shadcn 原语(56 个)
349
+ │ ├─ ui/index.ts # └ barrel
350
+ │ ├─ ai-elements/<name>.tsx # AI SDK Elements(48 个)
351
+ │ ├─ ai-elements/index.ts # └ barrel
352
+ │ └─ index.ts # ui + ai-elements 合并 barrel
353
+ ├─ lib/utils.ts # cn()
354
+ ├─ hooks/ # useIsMobile() 等
355
+ ├─ components.json # shadcn / ai-elements CLI 配置(add / diff)
356
+ ├─ index.ts # barrel:lib + hooks + components(含 ai-elements)
357
+ ├─ shadcn-env.d.ts # 本包 tsc 用的 *.css 环境声明
358
+ └─ styles.css
359
+ ```
360
+
361
+ 对外只有两个入口:
362
+
363
+ | 入口 | 内容 |
364
+ | --- | --- |
365
+ | `@openconsole/shadcn` | 全部 —— UI 原语 + 工具 + AI Elements,一行导入 |
366
+ | `@openconsole/shadcn/styles.css` | 主题与 token |
367
+
368
+ ### 核心不变量:无构建 ⇒ 相对导入
369
+
370
+ 本包**不构建**、直接发布 `.tsx`,由消费方 `transpilePackages` 编译。`@/` 这类路径别名**跨包不可解析**(在消费方会被它自己的 `@/` 接管),所以:
371
+
372
+ > **仓库里的源码一律相对导入**(`../../lib/utils`、`../ui/button`),不留 `@/`。
373
+
374
+ `components.json` `@/` 别名与 `tsconfig.json` 的 `paths` **仅供 CLI 解析**。`shadcn add` / `ai-elements add` 生成的文件会带 `@/`,需**手动**按文件位置改回相对路径:`@/lib/utils` `../../lib/utils`;引用 UI 原语 `@/components/ui/x`(或 `@/registry/default/ui/x`)在 ai-elements 里写 `../ui/x`、在 ui 内部写 `./x`。新增组件别忘了补进对应目录的 `index.ts` barrel。
375
+
376
+ ## 升级 shadcn 原组件
377
+
378
+ 组件与官方同名同位(`components/ui/<name>.tsx`),`shadcn diff` 原生可用:
379
+
380
+ ```bash
381
+ cd packages/shadcn
382
+ npx shadcn@latest diff <component> # 看官方 registry 与本地实现的差异
383
+ npx shadcn@latest add <component> --overwrite # 拉最新实现覆盖本地
384
+ # 手动:把生成文件里的 @/ 改成相对路径;若是新增组件,加进 components/ui/index.ts
385
+ pnpm typecheck # 校验
386
+ ```
387
+
388
+ > `diff` 的 import 行会恒显示差异(官方用 `@/`、本地用相对)—— 这是无构建分发的取舍,**看组件主体逻辑即可**。本包的品牌定制集中在 `styles.css` 的 CSS 变量、而非组件源码,故覆盖通常不冲突。
389
+
390
+ ## AI SDK Elements
391
+
392
+ [AI SDK Elements](https://elements.ai-sdk.dev) 的 48 个 AI-native 组件(Conversation / Message / PromptInput / Reasoning / Tool / CodeBlock / Sources …)已 vendor 进 `components/ai-elements/`,构建在本包的 shadcn 原语之上,与 UI 原语一起从根 barrel 导出:
393
+
394
+ ```tsx
395
+ import { Conversation, Message, PromptInput } from "@openconsole/shadcn";
396
+ ```
397
+
398
+ ### 依赖
399
+
400
+ AI 运行时依赖登记为**可选** peer(`peerDependenciesMeta.optional`):`ai`、`streamdown`(+`@streamdown/*`)、`use-stick-to-bottom`、`@xyflow/react`、`shiki`、`media-chrome`、`motion`、`nanoid`、`tokenlens`、`react-jsx-parser`、`@rive-app/react-webgl2`、`ansi-to-react`、`@radix-ui/react-use-controllable-state`。
401
+
402
+ ```bash
403
+ pnpm add ai streamdown use-stick-to-bottom # 聊天三件套示例:按需只装用到的
404
+ ```
405
+
406
+ > 根 barrel 含全部 48 个组件,TS 会解析其类型 ⇒ 消费方需装齐用到的 AI 依赖。`transpilePackages` 默认已覆盖本包;`canvas` 等带 `*.css` 副作用导入由打包器处理(Next 默认 OK)。源码已改写为 **ES2022 可移植**,消费方无需 `es2023` lib。
407
+
408
+ ### 升级
409
+
410
+ ```bash
411
+ cd packages/shadcn
412
+ npx ai-elements@latest add <name> # 或:npx shadcn@latest add https://elements.ai-sdk.dev/api/registry/<name>.json
413
+ # 手动:把 @/ 改相对;新增组件加进 components/ai-elements/index.ts
414
+ pnpm install && pnpm typecheck
415
+ ```
416
+
417
+ > 两处 upstream 与本包严格 tsconfig 冲突、需手改:`jsx-preview` 的 `Array.toReversed()` → `[...x].reverse()`(ES2022 可移植);`reasoning` 的 effect 补 `return undefined`(noImplicitReturns)。改完 `pnpm typecheck` 应为 0 报错。
418
+
419
+ ## 与 shadcn/ui CLI 的差异
420
+
421
+ | 维度 | `@openconsole/shadcn` | `npx shadcn add` |
422
+ | ---------- | ------------------------------------ | ----------------------------- |
423
+ | 分发方式 | 包,集中升级 | 拷贝到应用,分散维护 |
424
+ | 导入方式 | 平铺 `from "@openconsole/shadcn"` | 按文件路径 `from "@/components/ui/button"` |
425
+ | 样式 | `import "@openconsole/shadcn/styles.css"` 一行就位 | 拷贝 `globals.css` 后手工维护 |
426
+ | 升级 | 升级包 | 重新 add + 手工 diff |
427
+ | 跨应用复用 | 一份代码 N 个应用 | N 份相同代码 |
428
+ | 修改组件 | 改源码即可,monorepo 内立刻生效 | 自由度更高 |
429
+
430
+ ## 常见问题
431
+
432
+ **Q:为什么导入路径里没有 `/button` / `/card`?**
433
+ A:本包用嵌套 barrel 把 UI 原语与 AI Elements 都从 `@openconsole/shadcn` 一行导出,升级只改一处 `import`,深路径不受支持。
434
+
435
+ **Q:我能像 shadcn CLI 那样直接改组件源码吗?**
436
+ A:能。monorepo 内 `packages/shadcn/components/ui/<component>.tsx` 是直接源码,按 `transpilePackages` 配置编译到应用。改完立刻看到效果。但请注意改动会影响所有依赖该组件的应用,且会让该组件后续的 `shadcn diff` 噪音变大。
437
+
438
+ **Q:为什么需要传给 `<Icon>` 字符串而不是直接 React 组件?**
439
+ A:菜单 / 导航这类数据结构会被 RSC ↔ Client 间传递。React 组件不可序列化,字符串可以。把图标查表延迟到 Client 端做。
440
+
441
+ **Q:`useIsMobile()` 的断点能改吗?**
442
+ A:当前断点是 `768px`,硬编码在 [`hooks/use-mobile.ts`](./hooks/use-mobile.ts)。需要不同断点就直接用 `window.matchMedia` 自己写一个。
443
+
444
+ **Q:Tailwind v4 配置去哪了?**
445
+ A:v4 是 zero-config。本包通过 `styles.css` 里的 `@source "./**/*.{ts,tsx}"` 和 `@theme inline { … }` 完成等价的「配置」。应用层无需再写 `tailwind.config.js`。
446
+
447
+ ## 与 `@openconsole/atoms` 的关系
448
+
449
+ `@openconsole/atoms` 在本包之上做了更高层的组合:
450
+
451
+ | 层次 | 包 | 关注点 |
452
+ | ------------------------ | ------------------------ | ------------------------------------- |
453
+ | 设计 token + 原语 | `@openconsole/shadcn` | 按钮 / 输入 / 弹窗 / 分页… |
454
+ | 业务级骨架 | `@openconsole/atoms` | Header / Sidebar / Breadcrumbs / 错误页 / Preferences |
455
+
456
+ 只用得到原语:直接 `@openconsole/shadcn`。要做后台骨架:装 `@openconsole/atoms`(它已经把 shadcn 列为 peer 依赖)。
457
+
458
+ ## License
459
+
460
+ 参见仓库根目录的 LICENSE。