拓冰建站拓冰建站
首页 / 资讯中心 / 正文

@civitai/ui 组件库深度解析:基于 shadcn-svelte 的 Civitai 共享 UI 方案

civitai/ui 组件库深度解析基于 shadcn-svelte 的 Civitai 共享 UI 方案【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai本篇技术指南围绕 packages/civitai-ui/README.md 展开系统讲解 Civitai monorepo 中共享 UI 包civitai/ui的定位、架构设计与实战接入方法。你将掌握如何在一个新的 SvelteKit 应用中用四行配置接入该组件库、如何正确引用与扩展组件、以及theme.css、SelectionSet等核心模块的底层实现原理可直接用于apps/moderator及未来 SvelteKit 子应用的开发。1. 包定位monorepo 里 SvelteKit 应用的共享 UI 底座Civitai 主应用是 React Mantine 技术栈而 monorepo 中新增的 SvelteKit 应用如apps/moderator、未来的creator-hub则使用 Svelte Tailwind。为了让这些应用共享同一套 UI 基础仓库内构建了独立的civitai/ui包其定位清晰它是shadcn-svelte 共享组件 主题的组合包供 monorepo 内所有 SvelteKit 应用复用底层构建于bits-ui无头组件库、Tailwind v4、Svelte 5 之上采用Dark-only设计——全站只支持暗色模式不提供浅色主题分支。这一设计思路在 docs/packages/new-app-integration.md §0 中被明确为团队规范优先使用生态已有的组件而不是手写 UI 原语。shadcn-svelte 提供无障碍、Tailwind 样式的原语组件button、dialog、sheet、combobox、badge、checkbox、dropdown-menu、tooltip 等因此被作为首选只有 Civitai 特有的复合组件EdgeMedia图片渲染、NSFWImageGuard、瀑布流 masonry、审核工具栏等才值得手写且必须构建在civitai/ui原语之上。2. 核心架构Raw TypeScript 交付与自别名解析与其他civitai/*包一致civitai/ui的组件以raw无构建步骤形式交付即直接以.svelte/.ts源码发布由消费方应用通过 Vite 的ssr.noExternal进行转译。这一点从 package.json 的导出映射可以看得很清楚{ name: civitai/ui, version: 0.0.0, private: true, type: module, exports: { ./theme.css: ./src/lib/theme.css, ./utils.js: ./src/lib/utils.ts, ./utils: ./src/lib/utils.ts, ./hooks/*: ./src/lib/hooks/*, ./components/*: ./src/lib/components/* } }导出路径直接指向src/lib下的源码文件注意.utils.js实际上解析到.ts文件包内不产出任何 dist 产物。同时包内部所有组件都通过civitai/ui自别名相互引用。例如 selection-checkbox.svelte 中import { Checkbox } from civitai/ui/components/ui/checkbox/index.js; import { suppressShiftSelection, type SelectionSet } from civitai/ui/hooks/selection-set.svelte.js;button.svelte 中也使用civitai/ui/utils.js引入cn与WithElementRef。这些别名在civitai/ui包内可自解析在任意消费应用中也能解析到同一份源码从而保证组件代码无需任何改写即可跨应用复用。3. 从 SvelteKit 应用接入四步 Bootstrap在应用中使用civitai/ui的前提是应用必须运行在 Tailwind v4 上使用tailwindcss/vite插件。满足这一前提后接入只需四步。下面以真实消费方apps/moderator为例逐一说明。3.1 声明工作区依赖在应用的package.json中添加civitai/ui: workspace:*pnpm-workspace.yaml已包含apps/*与packages/*的通配因此pnpm install会自动将工作区内的包链接进来。3.2 配置 Vite 转译由于包以 raw TypeScript 交付需要在vite.config.ts中将其加入ssr.noExternal让 Vite而非 Node负责转译// vite.config.ts ssr: { noExternal: [civitai/ui] }apps/moderator/vite.config.ts 的完整配置更进一步它不仅把civitai/ui列入noExternal还列出了应用实际引入的全部civitai/*包civitai/auth、civitai/db、civitai/redis等并提供了process.env桥接 shim 与路由预热server.warmup.ssrFiles等工程细节可作为新应用的最小可运行模板。3.3 在全局样式中引入主题应用需要在src/global.css中按顺序引入 Tailwind、动画库与共享主题/* src/global.css */ import tailwindcss; import tw-animate-css; import civitai/ui/theme.css; /* palette shadcn tokens dark variant */ /* Tailwind v4 skips node_modules — make it scan this package, or the component classes get purged. */ source ../../../packages/civitai-ui/src/lib;其中最后一行source至关重要Tailwind v4 默认跳过node_modules如果不把civitai/ui的源码目录加入扫描范围包内组件用到的工具类会被 purge 掉导致样式全部丢失。apps/moderator/src/global.css正是这样实现的并在layer base中补充了color-scheme: dark、bg-background font-sans text-foreground等全局声明。注意tw-animate-css不随包分发它始终作为应用自身依赖存在——因为它是 CSSimport语法无法被包导出必须在消费方直接安装。3.4 标记根元素为暗色Dark-only 主题要求根元素带上dark类custom-variant dark依赖它生效!-- src/app.html — dark-only -- html langen classdark完成以上四步即完成整个 bootstrap——这也是 docs/packages/new-app-integration.md §0 所称的“四行接入”。4. 使用组件命名空间导入与工具函数civitai/ui的组件按 shadcn-svelte 惯例组织每个组件一个目录目录下含组件.svelte文件与index.ts出口。使用方式如下script import { Button } from civitai/ui/components/ui/button/index.js; import * as Dialog from civitai/ui/components/ui/dialog/index.js; import { cn } from civitai/ui/utils.js; /script几点要点单组件默认导出如Button直接从button/index.js导入组合组件用命名空间导入如Dialog这类由 Trigger/Content/Title 等组成的复合组件以import * as Dialog方式引用其下的Dialog.Trigger、Dialog.Content等避免大量具名导入工具函数独立路径cn等工具从civitai/ui/utils.js导入。以 button.svelte 为例可以看到 shadcn 组件的典型结构script langts module中通过tailwind-variants的tv()定义样式变体其中variant支持default/outline/secondary/ghost/destructive/link六种size支持default/xs/sm/lg/icon/icon-xs/icon-sm/icon-lg八种实例script中通过$props()接收属性并根据是否传入href自动渲染为a链接或button按钮同时用aria-disabled、tabindex正确处理禁用态。5. 添加 / 更新组件必须在包内执行 CLIcivitai/ui的设计准则是所有 shadcn 原语统一收进这个共享包而不是散落在各应用里。因此添加新组件时CLI 必须在包目录内运行而不能在应用里执行cd packages/civitai-ui npx shadcn-sveltelatest add name --overwrite --skip-preflight该命令读取包内的 components.json其别名全部指向civitai/ui/...{ $schema: https://shadcn-svelte.com/schema.json, tailwind: { css: src/lib/styles.css, baseColor: neutral }, aliases: { lib: civitai/ui, utils: civitai/ui/utils, components: civitai/ui/components, ui: civitai/ui/components/ui, hooks: civitai/ui/hooks }, typescript: true, registry: https://shadcn-svelte.com/registry }组件会落在src/lib/components/ui/name/下所需的新 npm 依赖也会安装进本包。由于消费应用已通过source扫描该包添加组件后应用侧无需任何改动。除了官方 CLI仓库还提供了定制的拉取脚本 scripts/add-components.mjs。它的存在是因为官方 CLI≥1.3默认指向一个失效的novaregistry 且init会覆盖自定义的theme.css因此该脚本直接从https://shadcn-svelte.com/registry拉取组件将组件 JSON 中的$UTILS$、$UI$、$COMPONENTS$、$HOOKS$、$LIB$等占位符按components.json的别名替换后写入src/lib/components/ui/并支持--all-free批量拉取所有无新依赖的registry:ui组件与--force覆盖已有组件参数。6. 包内组成与源码级解析按 README 的划分civitai/ui的内容可分为“CLI 生成的原语”与“手写代码”两类二者有明确的维护边界。6.1 CLI 生成原语切勿手改src/lib/components/ui/下是 shadcn-svelte 的 CLI 生成原语accordion、alert-dialog、calendar、combobox、command、context-menu、date-picker、dialog、dropdown-menu、field、hover-card、input、menubar、navigation-menu、pagination、popover、radio-group、select、sheet、sidebar、table、tabs、tooltip 等数十个。它们由--overwrite反复再生成因此永远不要手工编辑——任何定制都应通过变体、类名或在其之上组合实现。6.2 手写组件与工具手写部分放在原语目录的兄弟位置src/lib/components/selection/SelectionCheckbox组件含index.ts导出是 Gmail 风格 Shift 多选的具体 UI 载体src/lib/utils.tscn工具函数与类型辅助src/lib/theme.css共享主题src/lib/hooks/is-mobile与selection-set两个 hook。utils.tscn与类型辅助utils.ts 的实现是 shadcn 惯例的clsxtailwind-merge组合import { type ClassValue, clsx } from clsx; import { twMerge } from tailwind-merge; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }此外导出若干 Svelte 5 场景下的类型辅助WithoutChildT/WithoutChildrenT剔除child/children属性与WithElementRefT, U追加可选的ref属性后者被 button 等组件用于透传 DOM 引用。theme.cssMantine 派生调色板 shadcn tokenstheme.css 是整套视觉体系的源头结构分三层custom-variant dark将dark定义为:is(.dark *)使暗色变体只需根元素一个类即可全局生效theme调色板Mantine 派生的dark-0~dark-9、blue-0~blue-9、red-0~red-9、green-0~green-9十级色阶供应用自己编写的标记使用同时定义了--font-sans系统字体栈与--color-white#fefefe、--color-black#222222shadcn 设计 tokens:root中定义--radius: 0.625rem及 neutral 色系的--background、--foreground、--card、--popover、--primary、--secondary、--muted、--accent、--destructive、--border、--input、--ring、--chart-1~5、--sidebar-*等 CSS 变量.dark块中给出对应的暗色取值最后由theme inline把这些变量桥接为 Tailwind 的颜色工具类bg-background、text-foreground等。值得注意的细节文件末尾的layer base恢复了按钮光标——Tailwind v4 的 preflight 把按钮cursor设为default导致所有控件失去手型指针。这里通过button:not(:disabled)、[rolebutton]:not([aria-disabledtrue])等选择器统一恢复为pointer并让禁用态显示not-allowed一处声明覆盖全部应用与组件。hooksis-mobile与SelectionSetis-mobileis-mobile.svelte.ts基于 Svelte 5 响应式MediaQuery实现默认断点 768px即max-width: 767px时视为移动端可在构造时传入自定义断点。SelectionSetselection-set.svelte.ts是一个继承自SvelteSetK的响应式选择集实现了Gmail 风格 Shift 点击范围选择普通点击切换单个 key并将其设为锚点anchorShift 点击将锚点到点击 key 之间的整个区间都置为与点击 key 相同的选中状态然后更新锚点边界保护当锚点为空、位于当前可视列表之外如跨页或就是点击的 key 自身时退化为普通切换——防止用户选中看不见的行clear()会同时清空锚点避免旧锚点残留引发错误的区间选择。同文件还导出suppressShiftSelection(event)在onmousedown中preventDefault()避免 Shift 点击同时触发列表的文字选择。配套测试 selection-set.test.ts 覆盖了全部关键行为普通切换、向下/向上 Shift 区间选择、反向 Shift 取消整段、部分选中区间的 Gmail 行为、区间选中链式锚点更新、无锚点/跨页锚点回退以及clear()清除锚点等 10 个用例。SelectionCheckboxselection-checkbox.svelte把这些能力封装为表格行选择复选框接收selection: SelectionSetK、key: K、order: readonly K[]用户看到的 key 顺序决定 Shift 区间跨度通过bind:checked的函数绑定读写选中态并在onclick/onkeydown中捕获shiftKeybits-ui 会在自身 toggle 之前执行这些处理器最后用suppressShiftSelection阻止onmousedown的文字选择。6.3 未来的 Civitai 复合组件README 明确规划了civitai/ui的演进方向EdgeMedia、ImageGuard、masonry、审核工具栏等Civitai 特有复合组件将随时间沉淀进该包构建在上述原语之上而不是从零手写。7. 测试与 CI包的测试命令为pnpm --filter civitai/ui test即 vitest 运行 vitest.config.ts 中include: [src/**/*.test.ts]匹配的测试。配置里有一个关键细节ssr: { resolve: { conditions: [browser] } }——因为svelte/reactivity的默认导出是服务端构建其中SvelteSet只是普通Set而 Node 测试通过 Vite 的 SSR 解析器走会忽略顶层resolve.conditions所以必须在 SSR 解析条件中显式指定browser才能让测试运行在响应式客户端构建上selection-set.test.ts的第一个用例正是断言这一点。CI 中这些测试以 “Package unit tests” 任务运行。8. 相关文档docs/packages/new-app-integration.md新应用接入civitai/*共享包的完整指南包含process.envshim、Kysely 数据层、环境变量速查表等apps/moderator本仓库内首个消费civitai/ui的 SvelteKit 应用其 vite.config.ts 与 src/global.css 是可直接复制的真实接入范例。【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门