ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DeepChat dc-ui 设计组件层:从 DcButton 到 DcToast 的组件契约、替换范围与行为等价迁移实践

DeepChat dc-ui 设计组件层:从 DcButton 到 DcToast 的组件契约、替换范围与行为等价迁移实践 DeepChat dc-ui 设计组件层从 DcButton 到 DcToast 的组件契约、替换范围与行为等价迁移实践【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat本文以 DeepChat 仓库中 dc-ui 设计组件层的实施计划plan.md为主体完整讲解该组件层的目录组织、别名注册方式、八个核心组件的契约定义、settings/chat 两侧的替换范围以及第九轮 Action 收敛与行为等价约束。读完本文你将掌握如何在 Vue shadcn 技术栈中构建一层少而全的展示组件封装既复用现有 shadcn 原语又不改变任何事件、焦点与可访问性行为并能让 Tailwind v4 正确扫描到组件内的 class。配套文档可进一步参考spec.md组件契约与验收标准、tasks.md九轮实施任务清单、design-system.md设计系统总览、accessibility/spec.md可访问性契约。目录结构与别名注册dc-ui 是 renderer 展示层组件库位于src/dc-ui/与src/shadcn/平级导入路径统一走dc-ui/*别名。实施计划给出的目标目录结构为src/dc-ui/ ├── index.ts # 顶层导出 └── components/ ├── button/ DcButton.vue index.ts ├── icon-button/ DcIconButton.vue index.ts ├── status-pill/ DcStatusPill.vue index.ts ├── confirm-dialog/DcConfirmDialog.vue index.ts ├── toggle-row/ DcToggleRow.vue index.ts ├── empty/ DcEmpty.vue index.ts ├── skeleton/ DcSkeleton.vue index.ts └── toast/ DcToast.ts index.ts对照当前仓库实际目录src/dc-ui可以看到结构在计划基础上继续演进icon-button/在第九轮被DcButton吸收后移除同时新增了badge/、choice-group/、dropdown-action-item/、form/、form-actions/、inline-error/、popover/、section-card/、sheet-panel/、tooltip/等组件目录以及styles/motion.css动效令牌文件。这印证了实施计划每个组件拥有一种稳定交互、复用优先的原则——目录随真实调用方增长而不是预先堆砌。别名与构建扫描需要四处配套缺一不可Vite renderer 别名electron.vite.config.ts 的 resolve.alias 中追加dc-ui: resolve(src/dc-ui)。TS pathstsconfig.app.json 与根 tsconfig.json 的paths追加dc-ui/*与shadcn/*的写法保持一致。Tailwind v4 内容扫描src/renderer/src/assets/style.css的source追加../../../dc-ui/**/*.{vue,ts,tsx,js,jsx}否则 dc-ui 组件内部使用的 Tailwind class 不会被扫描生成样式会消失却不报错。测试别名vitest.config.renderer.ts 同样注册dc-ui否则单测中 import dc 组件会解析失败tasks.md 第六轮明确记录过这一修复。为什么必须让 Tailwind 扫描到 dc-uiTailwind v4 默认只扫描项目内被source覆盖的文件。dc-ui 的组件把布局 class如h-7 px-2.5 text-xs硬编码在.vue模板与 cva 变体里这些文件不在 renderer 默认扫描路径内。计划中专门强调这一点否则 dc-ui 的 class 不会被扫描是这类跨目录组件库最容易踩的坑开发时看起来一切正常切换主题或增量构建后个别组件样式缺失。组件契约总览计划为每个组件定义了 props、事件、插槽与视觉基线。下面逐组件展开并结合当前源码验证契约的实际落地情况。DcButtonButton 的唯一收敛点契约定义见 DcButton.vue 与 props.ts透传 variant/size保留default / outline / ghost / destructive / linksecondary、lg、icon-lg不在 props 类型中显式暴露但 cva 变体中实际存在需要时仍可透传。新增sizexsh-7 px-2.5 text-xs用于收敛代码库中 20 处手工覆盖的小号按钮。源码中 xs 档完整为h-7 gap-1.5 rounded-md px-2.5 text-xs has-[svg]:px-2props.ts带 icon 时自动收紧水平 padding。iconSize3 | 3.5 | 4默认4通过 computed 映射为size-3 / size-3.5 / size-4classDcButton.vue作用于内联 svg 尺寸。loading内建 Spinner 替换 icon 区icon直接传 Iconify name替代手写Icon节点。间距规则icon 文字用 flexgap-1.5不再叠加mr-*替换调用方时必须移除手写的mr-1/mr-2。可访问名label只提供aria-label可访问名不创建 tooltiptooltip才渲染可见提示并在缺省 label 时作为兜底名DcButton.vue。开发期校验DEV 环境下 icon-only 按钮size 命中icon/icon-sm/icon-xs/icon-lg若既无label也无tooltip打印console.warn提示可访问性缺失DcButton.vue。cva 基础类还包含动效与焦点样式active:scale-[0.97]、motion-reduce:active:scale-100、focus-visible:ring-[3px]动画时长走--dc-motion-fast动效变量props.ts与styles/motion.css的令牌体系对应。第九轮的关键演进DcIconButton被DcButton完全吸收——icon / tooltip / label / loading / active / 默认插槽统一由 DcButton 承载DcIconButton文件已删除。DcButton 的 tooltip 契约随之扩充tooltipSide、tooltipSideOffset默认 4、tooltipDelayDuration默认 0保持原 DcButton 立即弹出的行为、tooltipContentClass、tooltipIgnoreNonKeyboardFocus默认 true并由内建TooltipProvider :delay-duration200提供全局兜底DcButton.vue。DcStatusPill静态 Badge 与运行时状态分离DcBadge表示静态元数据DcStatusPill表示运行时状态两者语义分离是设计系统里的硬约束spec 中Overlay and feedback contract一节。propsstatus六种语义态neutral|active|success|warning|danger|disabled、label、showDot默认 true、pulseloading 态圆点animate-pulse、sizesmtext-xs /xstext-[11px]。结构inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 语义圆点。颜色映射DcStatusPill.vuesuccessemerald、warningamber、dangerred、activeprimary、disabled/neutralmuted 系全部带dark:变体。源码中一个值得注意的细节是status 别名归一化normalize()函数把running→success、error/auth-error→danger、auth-required→warning、loading→active、offline/stopped→neutralDcStatusPill.vue。计划原文只列出了running|loading|error|offline别名实际实现又为 MCP 场景补了auth-required与auth-error——tasks.md 第四轮记录了一次因缺auth-required导致pnpm run typecheck:web失败并修复的过程说明别名集是随真实调用方增长的而非一次定死。典型调用方是 MCP 服务器卡片running/loading/error/auth-required/stopped五种服务器状态经别名直接映射到语义色不再手写bg-*类。DcConfirmDialogAlertDialog 异步确认的完整封装契约要点与源码对应DcConfirmDialog.vuepropsopenv-model、title、description?、icon?、danger默认 true确认按钮走destructive、confirmLabel?/cancelLabel?缺省取 i18ncommon.confirm/common.cancel见 DcConfirmDialog.vue、busy、disabledConfirm、confirmIcon?第三轮又追加confirmAttrs/cancelAttrs/busyDataTestid透传用于保留调用方的data-testid等测试钩子。事件confirm可返回 Promisepending 期间双按钮禁用、确认按钮显示 busy Spinnercancel在打开态变化为关闭时一并触发。插槽default如错误信息rolealert行、actions完全自定义底部按钮组。容器w-[calc(100vw-2rem)] max-w-md在移动端窄屏下仍留边距。实现上还有一个计划未明说但源码可见的行为焦点恢复——busy变 true 时记录document.activeElementbusy 结束后若焦点丢失到 body 且原触发元素仍在文档中则focus({ preventScroll: true })还原DcConfirmDialog.vue。这正是 spec 中Confirmation dialogs preserve ... focus restoration验收项的落地。替换面settings 侧系统提示词/自定义提示词的删除确认加上后续轮次扩展的 MemoryListView、DataSettings、OcrSettings 等 8 个原AlertDialogAsyncAction文件。DcToggleRow统一单行/双行两种开关行源码propsid、icon?、label、description?、modelValue、disabled、ariaLabel?缺省回落到 label、labelMinWidth?。emitupdate:modelValueSwitch 的indeterminate值被归一为false插槽trailing替换右侧区域Switch 之前。布局规则无 description 时单行h-10有 description 时切换为纵向双行label 行 pl-7 text-xs text-muted-foreground说明行用一个组件统一了代码库中两种现存的开关行变体DcToggleRow.vue。实际调用方如 CommonSettings、AutoCompactionSettingsSection、DisplaySettings 均迁移到DcToggleRow DcSectionCard组合tasks.md 第二轮。DcEmpty、DcSkeleton、DcToastDcEmptyshadcn Empty 封装propsicon?、title、description?插槽default正文、action主行动容器统一border border-dashed rounded-lg居中布局。典型场景是自定义提示词列表的空态。DcSkeletonpropswidth/height默认100%/1rem任意 CSS 值、rounded?默认md、class透传背景使用bg-muted/40-70渐变层级沿用现有骨架惯例。DcToastDcToast.ts纯 TS 适配层DcToast.success/info/warning/error({ title, description?, code? })。code缺省时用crypto.randomUUID()生成不新增 duration 参数——时长策略完全交给通知引擎按 kind 决定。内部仅调用renderer-notifications/rendererNotificationPort的notifyRenderer不新增通知引擎、不覆盖时长策略是写死在契约里的边界。// src/dc-ui/components/toast/DcToast.ts 的核心逻辑原文继承 const notify (kind: TransientNotificationKind, options: DcToastOptions) { notifyRenderer({ kind, code: options.code ?? crypto.randomUUID(), title: options.title, description: options.description }) } export const DcToast { success: (options: DcToastOptions) notify(success, options), info: (options: DcToastOptions) notify(info, options), warning: (options: DcToastOptions) notify(warning, options), error: (options: DcToastOptions) notify(error, options) }第六轮中dc-ui 的 Toast 还承担了架构清理职责renderer 内联操作反馈机制InlineOperationFeedback / useSurfaceFeedback / surfaceFeedbackController 等 4 个文件被整体删除prompt 管理链路与其余 28 个调用文件的成功/失败提示统一收敛到notifyRenderertoast全量pnpm run test:renderer238 文件 / 1958 用例全绿tasks.md 第六、七轮记录。替换范围settings 与 chat-main 两个试点面计划划定的首批替换范围刻意选择高价值、模式重复的文件而不是全仓扫荡settings提示词管理全量PromptSetting.vue页面操作按钮 → DcButton含iconSize定制。prompt/SystemPromptSettingsSection.vue新建/重置/删除按钮 → DcButton/DcIconButton删除确认 → DcConfirmDialogdanger busy「启用」pill →DcStatusPill(active)。prompt/CustomPromptSettingsSection.vue新增/编辑/删除 → DcButton/DcIconButton DcConfirmDialog状态 pill →DcStatusPill(active/disabled)加 neutral 来源标识空态 → DcEmpty启停 Switch 保持原样dc 层不重复封装 Switch复用 shadcn 原语。prompt/PromptEditorSheet.vue/SystemPromptEditorSheet.vuefooter 按钮 → DcButton。chat-main高价值组件ChatTopBar.vueghost 型 icon 按钮 → DcIconButtonlabel tooltip。WindowSideBar.vueicon 按钮 → DcIconButton。components/message/MessageToolbar.vue5 个 Tooltip 包裹按钮 → DcIconButton注16px 超紧凑按钮曾被记为例外保留第九轮 review 后以icon-xs尺寸 原尺寸还原的方式重新迁移。components/mcp-config/components/McpServerCard.vue状态区 → DcStatusPillrunning/loading/error/auth-required/stopped 映射。后续的 Sheet/Popover 收敛同样遵循保留既有行为原则DcSheetPanel appearanceplain吸收原DcSheetDialogMcpServers 详情 Sheet 保留原 width、padding、ScrollArea、footer 与空 description 的 DOM/ARIA 结构DcPopoverPanel收敛为DcPopoverMcpIndicator 保留受控 open、trigger、header 与定位tasks.md 第九轮、plan.md 第九轮条目。数据流与行为等价约束dc-ui 的定位在 plan 中被一句话锁定dc-ui 是 renderer 展示层封装不触碰 store / IPC / shared。组件只拥有展示、可访问性与共享状态业务操作、持久化、IPC 仍归功能模块所有spec 的 Purpose and ownership 同义重申。在此定位下迁移必须逐项保持行为等价计划给出了四条硬约束原事件、修饰符与 payload 不变click/select/.stop/.prevent不得被组件吞掉、改名或重发。实现上 DcButton 设inheritAttrs: false全部$attrs直接落到最内层Primitive元素上data-testid等业务钩子随 attrs 自然落在原本可交互的 DOM 上。原禁用条件、type、loading、Popover/Dialog 开闭、键盘及焦点行为不变缺失 tooltip 时只为可操作的 icon-only 控件补充并复用既有 i18n 文案同时作为label。原 tooltip 的文案、side、delay、条件和嵌套触发器结构不变DcButton因此保留tooltipDelayDuration默认 0 与ignoreNonKeyboardFocus默认 true 两个兼容默认值而非强加统一时序。纯展示 icon、文字按钮、说明型 Switch/Checkbox/链接 tooltip 不为统一形式而改变交互——收敛只发生在真正有操作语义的控件上。spec 的验收章节还要求保留事件修饰与 payload、原生元素类型、disabled 条件、loading、overlay 状态、键盘行为、焦点、i18n key 与测试钩子属性转发到拥有控件的元素而不吞掉业务事件新行为必须满足 accessibility/spec.md 的可访问性契约。验证策略与质量门计划给出的验证策略原文保留每一处迁移先人工对照原模板事件、修饰符、disabled、type、loading、测试钩子、tooltip 契约。本阶段不运行 vitest / lint / fmt / typecheck仅在要求提交时按受影响范围统一执行。提交前人工检查主窗口与设置窗口中按钮、弹窗、tooltip 的可见交互与深浅色样式。实际执行中tasks.md验证逐步升级为可重复的质量门每轮结束跑pnpm run typecheck:web并真实捕获过类型错误DcStatusAlias 缺auth-required、DcSheetPanelopen可选化、McpIndicator 模板 v-if/else 配对等。第六轮全量pnpm run test:renderer238 文件 / 1956 用例全部通过期间修复了 vitest 别名缺失与 DcConfirmDialog teleport 所需的 AlertDialog stubs。第七轮再全量验证typecheck 与 1958 用例全绿。主窗口 ChatMainApp 根加全局TooltipProviderdelay 200 ignore-non-keyboard-focus与 settings App.vue 对齐后新代码无需再局部包裹11 个既有局部 Provider 因 delay/ignore 配置不同而保留为显式覆盖第八轮。小结一套可复用的组件层治理路径把 plan.md 的脉络串起来dc-ui 组件层给出的是一套可迁移的组件层治理方法先定契约再建组件每个组件的 props/事件/插槽/视觉基线先写在 plan 里源码只是契约的兑现对照 docs/features/dc-ui-components/plan.md 与 src/dc-ui 可以逐项核对。别名 扫描四处配套vite alias、两个 tsconfig paths、Tailwindsource再加 vitest alias——任何一处漏配都会产生能跑但样式缺失/测试挂的隐蔽问题。收敛而非新增第九轮删除DcIconButton、DcSheetDialog、DcPopoverPanel并入更通用组件目录从按计划建变成按调用方长spec 明确要求Shared APIs grow only for actual callers。行为等价作为迁移准入门槛事件修饰符、焦点、testid、i18n key、tooltip 时序逐项保留配合 typecheck vitest 双质量门让展示层重构不需要业务侧回归成本。对想在同类 Electron Vue shadcn 项目中建立展示组件层的读者这份 plan/spec/tasks 三件套与src/dc-ui源码本身就是可直接对照的样板契约文档先行、替换范围按高价值重复模式切块、每轮以类型检查与组件测试收口。【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表