English
工作台 UI 统一与共享设计基础
更新:2026-10-09。已接入 YunLeFun 的 CSS 设计基础,采用 Workspace / Admin(ToB) 的中性色、系统字体和控件密度;保留 Craft Hub 的工作台布局、源码组件与 Reka UI 交互。
为什么选择 Workspace / Admin
YunLeFun/design 将使用场景分为 Consumer 与 Workspace,Workspace 下进一步区分 Admin / Editor。个人使用并不意味着必须选 Consumer:Craft Hub 的主要任务是查看软件与环境状态、配置工具、审阅安装和迁移计划,适合 Admin 的查询和操作密度。Consumer 更偏内容、品牌与轻操作;Editor 更适合画布、时间线和属性面板。
默认正文 14px、辅助文字 12px、页面标题 22px;共享控件桌面默认 32px、紧凑 28px,窄屏或粗指针增至 44px / 40px。控件圆角 6px、面板 10px。保留现有留白、卡片和局部玻璃效果,避免让工作台变成密集数据表。标题栏和侧栏部分历史控件仍有独立尺寸,并非所有导航点击区域都已统一。
实际接入方式
- 使用 pnpm catalog 固定
@yunlefun/ui@0.0.7,依次导入已发布的@yunlefun/ui/css与@yunlefun/ui/admin.css,随后导入本地语义 token。不再暂存上游 CSS;Craft Hub 的覆盖只写在styles/tokens.css。 - MIT 许可保存在 Web 的 public/licenses 中,随 Vite 产物和桌面 Web 资源分发。
styles/tokens.css将表面、文本、品牌、焦点、状态、字体和间距映射到上游变量;保留工作台页面几何和特定业务状态。根元素的.ylf-theme-light/.ylf-theme-dark与原有data-theme由同一主题偏好驱动,body 下的 Portal 继承同一套主题。- 状态文字、实色填充和填充前景分别映射,避免把状态文字色直接用作按钮底色。共享按钮、输入和选择控件消费密度变量,工作台规则不覆盖 Button 的 size 接口。
此次采用 CSS-only,不引入上游 Vue 包、Sass、JS 运行时或远程字体。基础交互继续使用现有的 Button、Badge、Alert、DialogShell 和表单组件;长内容弹层保留固定头尾和滚动内容。
软件与环境图标
使用已安装的 UnoCSS / Iconify 管线,并固定本地开发依赖 @iconify-json/logos@1.2.15、@iconify-json/simple-icons@1.2.99。两套 collection 的声明许可均为 CC0-1.0;应用保留可见软件名称,图标作为装饰。
| 场景 | 图标来源 |
|---|---|
| Git、Node.js、pnpm、Homebrew | Logos 彩色品牌标识 |
| Claude、Gemini | Logos 彩色品牌标识 |
| ChatGPT、Perplexity、Raycast | Simple Icons,单色跟随当前文字颜色 |
| fnm、Workstation、Orca | 当前集合无已核实的对应标识,保留终端或工作区语义图标 |
| 自定义网站 | 按实际 HTTPS 主机名精确匹配;未知地址使用网页图标 |
只 safelist 并编译实际使用的图标为 CSS / SVG data URI,浏览器无需访问 Iconify CDN,也不加载完整 collection。彩色标识用 contain 保留比例;不通过用户可编辑的标题或 ID 推断品牌。现有插件声明的自定义图标仍优先保留。
来源:Iconify Logos、Simple Icons。
已完成与后续验收
本轮验证:pnpm lint、根目录及 Web typecheck、全量 pnpm test --run(926 通过、2 个平台相关跳过)和 pnpm build 通过。浏览器插件未提供,使用已有 Playwright 执行验收。抽查正文/辅助文字、品牌按钮及 warning/danger/success 的文字、实底与 hover 配对,明暗主题最低对比度 4.82:1;这不代表所有历史控件已完成无障碍审计。
生产 CSS 中九个品牌规则合并为 30.44 KiB,独立 gzip 10.46 KiB;发布基础 CSS 为 11.79 KiB,独立 gzip 2.35 KiB。独立压缩数值不等于整个安装包的增量,完整图标集合未进入 Web 运行时。
日常工作流的界面约定
2026-10-12:待办聚合、项目目录与交接弹层沿用同一套 Workspace / Admin 基础。参考 Apple 的列表、工具栏和布局层级,字体及控件尺寸仍以现有 YLF profile 为准;这不是原生 SwiftUI 控件替换。
- 每个工作来源使用一个中性表面,来源标题与账号名称在组头,事项按连续列表排列。相邻行用完整分隔线,避免每条事项重复边框和阴影。列表最小行高桌面 56px、手机 64px,左右留白使用共享间距。
- 事项标题 14px、状态和截止时间 12px、线性语义图标 16px。显示本地化的可读时间,原始时间放在
time[datetime]与提示里;不把账号 ID 或显示 ID 当作主要信息。保留加载范围、上次更新时间和失败后的过期提示。 - 项目目录同样使用连续列表,项目名称在首行、路径在辅助行。点击主体进入项目,尾部只保留交接图标动作,避免两个等权文字按钮竞争。
- 交接弹层按事项摘要 → 项目与关联范围 → 目标 → 复制上下文排列。项目和范围在桌面并排、手机堆叠。未选择使用 placeholder,显式“无需项目”独立表达。关联保存和清除在紧凑操作行,完整交接文本默认折叠,复制失败时展开供手动选择。
- 表单和按钮使用共享 Field、FormSelect、Textarea 与 Button;工具入口仍是可用的真实链接。固定头尾、内容滚动,头尾使用中性表面与内容形成层级。颜色、字体、焦点、圆角和暗色主题均消费已有 token,不增加另一套局部主题。
实现落在共享 daily-workbench.css 和上述三个通用组件中,个人总览与插件工作台保持一致。验收需覆盖明暗主题、1440px 桌面及 390px 手机,包含真实选择、复制、分页、关联保存与重载恢复,并检查键盘焦点、Portal 主题和横向溢出。
本轮验收:隔离演示来源下,Chromium 完整流程和 WebKit 手机暗色弹层通过;1440×1000、390×844、320×720 无页面横向溢出。手机列表主体、选择器与上下文展开入口至少 44px,长上下文由正文区域独立滚动,头尾按钮保持可见。Portal 跟随明暗主题,键盘焦点为 2px;侧栏 aria-selected、active 样式和正文一致,截图在颜色过渡结束后采集。截图复查覆盖字阶、连续行、组头留白、固定头尾、共享选择器、暗色表面及移动点击区域。没有新增远程字体或运行时 UI 依赖。
公开边界、lint、根目录 TypeScript、Web Vue 类型检查与完整构建通过;177 个测试文件通过,1103 项通过、2 项跳过。浏览器插件未提供,使用工作区已有 Playwright;本轮不计作原生 Electron 安装版或 iOS 真机验收。
按钮校准:页面与 Portal 直接使用共享 Button 的 500 字重、无外扩阴影和 16px 默认 / 14px 紧凑图标,不再由工作台容器单独覆盖。弹层默认文字动作至少 72px 宽,较长标签按内容扩展;同组动作共用高度、字号、内边距和垂直对齐。清理弹层里独立的 40px 高度规则,密度统一交给 profile。Chromium 桌面测得两个动作均为 32px 高,手机和 WebKit 均为 44px,复制与关闭验证通过。
交接弹层的成对动作进一步统一比例:两个文字按钮在桌面等宽,最小各 112px;手机填满底部并等分。移除复制按钮的图标,以颜色表达主要动作;成功后文案切换为再次复制,按钮不改变宽度。此布局由共享 dialog-action-pair 提供,按钮本身继续使用 profile 的 32px / 44px 高度,不强制其他复杂弹层的操作区采用两列。
成对布局验收覆盖 Chromium 1440 / 390 / 320px 和 WebKit 390px 暗色:两个动作的实际宽高与顶部位置一致,中文和英文复制成功前后尺寸稳定,按钮文字放大至 200% 时仍等宽同高且不裁字;焦点、复制完整身份上下文、关闭和 Escape 行为通过。截图按整体比例复查,而非仅以高度相等作为设计通过条件。
动效与桌面宿主行为
2026-10-15:对齐 YunLeFun/design main 新增的移动端与动效约定。styles/tokens.css 以 --motion-* 别名读取上游 --ylf-* 变量,调用方只用 --motion-*。2026-10-17:@yunlefun/ui@0.0.7 已发布这些变量,取值与原本地回退一致,回退值随之移除。上游在减少动态效果时把时长归零、按压缩放设为 1;全局规则原本就将时长压到 0.01ms,实际表现不变。
- 按压:共享 Button 按下 100ms 缩至 0.98,松开用软弹簧回弹;悬停反馈只在
hover: hover且pointer: fine时出现,触屏依赖按压与选中状态。 - 开关:拇指位移改用软弹簧(480ms),按下时向行程方向拉长 4px。
- 弹层:所有 Reka 定位弹层(菜单、Select、Combobox、Tooltip、Popover)在
[data-reka-popper-content-wrapper]下统一从触发方向轻移 4px、淡入,收起时淡出,开合对称;菜单使用 160ms,接近原生菜单的即时感。居中 Dialog(含默认.dialog-content)使用 240ms 进入、160ms 离开,命令面板保持 160ms。 - 减少动态效果:沿用全局
prefers-reduced-motion规则,并将位移、缩放和拇指拉伸归零;状态仍直接反映最终结果。 - 桌面宿主(Electron 或原生 macOS 宿主注入
data-desktop-platform):按钮、标签页、菜单项、开关等控件使用系统箭头光标且标签不可选中;侧栏、状态栏与面包屑不可选中,控件内图标与链接不可拖拽。链接、输入框、代码和正文保持浏览器行为;普通浏览器继续使用 Web 习惯的手形光标。
本轮在浏览器中验证:变量解析、弹簧曲线有效、菜单弹层方向与变换原点、退出动画结束后卸载并把焦点交还触发器、桌面模式下所有按钮均为箭头光标。尚未在 Electron 安装版与 Tauri 中做原生验收。
加载占位与内容进场
2026-10-15:首页同时读取多个来源,原先每块都显示转圈和「正在读取…」文字。改为与结果同形的骨架屏,减少整页转圈。
- 何时用骨架:首次加载、区域内还没有任何结果时,用
components/ui/skeleton的SkeletonList。行数、图标位、标题与说明行和真实列表一致,结果到达后不跳动。已有结果的刷新不替换为骨架:保留旧内容,只让刷新按钮转圈,并给读屏器一条隐藏的状态文字。按钮内的操作中状态(分析中、保存中)保持原位转圈。 - 可访问性:外层
role="status"+aria-busy="true",只朗读一句标签;占位形状aria-hidden。 - 外观:底色为
--skeleton-base(文字色 7% 混入表面色,深浅主题都可见),所有形状共用一道视口宽的高光,整页同步缓慢扫过。骨架在 120ms 后才淡入,快速响应不会闪一下。减少动态效果时去掉高光,只留静态占位。 - 进场:替换骨架的内容用
.motion-reveal(单块)或.motion-reveal-list(列表)上移 4px 淡入,列表行间隔 30ms,最多错开 6 行。动画只保留 backwards 填充,结束后元素不带 transform,不影响弹层定位。
已应用:今日重点、我的工作(来源列表与各来源首次加载)、常用项目、项目目录、工作区项目列表(替换原有私有闪烁样式)。
控件状态动效
- 分段标签:共享
TabsList的 segmented 变体渲染 RekaTabsIndicator,选中底板以 480ms 软弹簧在分段间滑动,替代原先背景瞬切;sidebar 变体与asChild用法不变。列表始终为position: relative,指示器首次测量即对齐。 - 导航项:背景、文字色、阴影 160ms 过渡;悬停只在精确指针下出现,与 Button 一致。
- 复选框:底色与边框 160ms 过渡,按下缩至 0.9;对勾只在用户改变状态后以软弹簧弹出,列表首次渲染时已勾选的项不播放。
- 今日重点的展开项:箭头旋转过渡;支持
::details-content与interpolate-size的引擎(Electron / Chromium)高度平滑展开,其他浏览器直接展开。
在浏览器中人为延迟接口验证:骨架行与结果行对齐,高光运行,两组分段标签的指示器与选中项像素对齐,切换时从 316 回弹到 190 后停在 199。深色主题未做截图验收。
macOS 窗口材质
2026-10-15:保留 Web UI,在宿主层让 macOS Electron 窗口更接近原生。主窗口启用 vibrancy: 'sidebar'、visualEffectState: 'followWindow' 与透明背景;失焦时材质随窗口切到非活跃状态,与系统侧栏一致。Windows 与 Linux 不变。
- 范围:只有活动栏与主侧栏(
.project-rail内的.activity-rail、.rail-content)透出材质。顶栏、状态栏、正文、二级分区导航、Dialog 与弹层保持不透明。正文区域旁的容器用零优先级:where()兜底为--surface,已有背景不受影响。 - 开关:Electron 主进程通过
--craft-hub-window-material=sidebar告诉 preload,preload 暴露craftHubDesktop.windowMaterial,Web 再写入data-desktop-window-material。CSS 同时要求data-desktop-platform='darwin'和这个属性。原生 macOS 宿主同样上报darwin,但没有这个属性,因此不会出现透明而无材质的区域。普通浏览器两个属性都没有,宽度不超过 760px 时也不生效。 - 着色:侧栏在材质上叠加原有颜色。浅色叠 75%,深色叠 60%;开启「减少透明度」时为 100%。浅色窗口后方是黑色窗口时最暗,实测材质约为
rgb(200 204 207)。叠 60% 后侧栏文字对比度为 4.39:1,低于 4.5:1,因此浅色提高到 75%:实测 4.65:1,图标 3.20:1。深色叠 60%:文字 7.80:1,图标 5.46:1。 - 启动:窗口以
show: false创建,ready-to-show后显示。加载失败时也会显示,以便诊断。非 macOS 的窗口底色改为 Web body 色(深色#191c22,原为#1f232b),首帧前不再出现色差。 - 右键菜单:Electron 默认没有右键菜单。输入框现在弹出原生编辑菜单:拼写建议、撤销、重做、剪切、复制、粘贴、粘贴并匹配样式、全选,可用状态取自
editFlags,文案跟随工作台语言。只读文字有选区时只显示「复制」。Reka 自定义菜单会取消 DOM 事件,原生菜单不会叠加出现。
验收:pnpm dev 依赖 4318 端口和开发会话锁,二者已被另一个 pnpm dev:web 占用。生产模式又会注册正式的 URL 协议,所以未直接运行。改用临时 Electron 脚本,复用真实的 window-appearance.ts 与 preload.cjs,加载同一份 Vite 页面,在 1440×900 下检查首页和项目页:
- 两种主题下
html/body透明,侧栏为半透明色。侧栏外 20 个采样点都落在不透明背景上。 - 输入框弹出完整编辑菜单;侧栏行右键只出现应用自己的菜单。
- 深色启动在显示后 80ms 内已是带样式的深色页面。显示瞬间的白帧在纯红和不透明对照窗口中同样出现,属于截图时机问题,不是闪白。
- 原材质对照:浅色在无背景、白色、黑色窗口后方分别为 207、225、200;深色为 43、83、38。材质跟随
nativeTheme.themeSource。 - 普通浏览器下根节点没有宿主属性,背景与改动前一致。
pnpm lint、pnpm typecheck、pnpm test --run(217 个文件,1730 通过,2 跳过)、pnpm build 通过。未测量 GPU 与能耗;材质由系统合成,页面没有新增 backdrop-filter。尚未在打包安装版、外接显示器以及开启「减少透明度」的系统上做原生验收。
启动包体与窗口状态
2026-10-17:首屏只保留首页、项目页和外壳,其余视图与弹窗按需加载,桌面窗口记住上次的位置与大小。
- 按需加载:
App.vue在模块级的<script lang="ts">中用lazyComponent()包装工具、软件中心、环境、诊断、集成、插件、工作台总览、欢迎页等视图,以及命令面板、设置、快速记录、Agent 操作、市场等弹窗。包装必须放在模块作用域;放进<script setup>会在每个实例上重建,预取的结果用不上。 - 空闲预取:挂载后
prefetchLazyComponents()每个空闲时段加载一个分块(requestIdleCallback,2 秒超时;不支持时 200ms 间隔)。预取经由异步组件自身的加载器完成,首次打开同帧渲染,不再出现 200ms 空白。预取失败会被忽略,组件首次渲染时自己重试。 - 弹窗:
useOpenedOnce()让弹窗在第一次打开时才挂载,之后保持挂载,关闭动画和内部状态不丢。 - 结果:Web 主包从 1,073 KB(gzip 304 KB)降到 460 KB(gzip 128 KB)。测试用
loadLazyComponents()在beforeAll中一次性解析,路由断言仍可同步进行。 - 窗口状态:Electron 主窗口关闭时把非最大化时的位置、大小和最大化状态写入用户数据目录的
window-state.json,下次启动恢复。标题栏所在区域不再落在任何显示器上(如外接屏已断开)时改为居中;尺寸限制在最小值与最大工作区之间。文件缺失或损坏时用默认 1440×900。 - 桌面根节点:
data-desktop-platform下根节点与 body 设overscroll-behavior: none,整窗不再橡皮筋回弹,内部滚动区保持原有回弹。macOS 宿主开启-webkit-font-smoothing: antialiased,与 AppKit 的灰度抗锯齿一致,深色模式下浅色文字不再发虚加粗。
在浏览器中验证:深色主题下首页、工具、项目页显示正常,懒加载的工具页切换无空白帧,控制台无错误。窗口状态只有单元测试覆盖,尚未在 Electron 中实际验收多显示器场景。
窗口焦点与滚动边缘
2026-10-17:补两处 macOS 原生窗口的细节。
- 非活跃窗口:桌面宿主下
window-focus.ts根据document.hasFocus()在根节点写入data-window-inactive(焦点在内嵌 frame 中仍算活跃)。窗口失焦时,活动栏、主侧栏和页面二级侧栏内的选中项去掉强调色:在这些容器里把--accent、--accent-soft与--workbench-sidebar-active-background改为中性灰(文字 8% 混入背景),与 AppKit 非活跃侧栏一致。弹窗、正文与普通浏览器不受影响。 - 滚动边缘分隔线:路径栏(面包屑)与页面标题区的下边线默认透明,内容滚到它们下面时才以 160ms 淡入,与 macOS 工具栏的滚动边缘一致。
WorkbenchPageShell在捕获阶段监听滚动,只认与内容区顶部齐平的滚动容器,页面中部的嵌套面板不会触发;切换路由时复位。WorkbenchViewFrame标题固定,分隔线画在其下方的整宽布局上;带二级侧栏的页面保留常驻分隔线。
在浏览器中验证:首页滚动 300px 后标题下方出现分隔线,回到顶部消失;模拟非活跃窗口时,选中项文字由 #2563eb 变为 #626b78,背景改为 8% 中性灰。浏览器面板截图不能反映过渡后的画面,最终观感仍需在 Electron 中确认。
侧栏收起与待连接来源
2026-10-17:
- 侧栏收起:活动栏宽度由
useTransition在 176px 与 52px 之间以 220ms easeOutCubic 过渡,CSS 变量--workbench-activity-width与项目页的 Reka 分栏偏移逐帧跟随。收起时保留展开时的几何:按钮仍满宽、高 32px,导航内边距不变,图标位置只移动约 1px。标签不再display: none,先淡出(展开时延迟 100ms 后淡入),因此宽度变化期间不重排、不出现横向滚动。窄屏与「减少动态效果」直接切换。浏览器逐帧采样:首页 176→52px 约 200ms 完成,图标横坐标保持 17–18px;项目页分栏宽度与活动栏同步变化,记住的侧栏宽度不受影响。 - 待连接来源:首页「我的工作」里,一个来源组的全部列表都需要授权时,只显示「需要连接账号」提示和连接按钮,不再渲染空的子标签、空列表和「查看全部」;组头或来源栏的刷新按钮仍在。刷新时保留上次的连接状态直到新的检查返回,提示不会闪成空列表。
「在 Craft Hub 中打开」弹窗
2026-10-17:craft-hub://project 链接在本机找不到已注册项目时弹出的确认框改用共享 DialogShell(panel 布局),由 Reka 提供焦点陷阱、Escape 和关闭后焦点归还;校验或注册进行中时不响应关闭。
- 层级:标题区左侧为 40px 品牌标记,右侧为标题与说明。仓库与子目录放在一张浅色 key-value 卡片里,行间用分隔线而非独立边框;值用等宽字体单行截断,完整内容在
title中。仓库显示为「主机/路径」(去掉协议、git@前缀和.git),旁边的复制按钮复制原始 URL,成功后图标以软弹簧切换为对勾,并通过隐藏的role="status"朗读。 - 提示:安全说明改为 12px 辅助文字,底色是强调色 6% 混入表面色、无描边,图标沿用未信任盾牌。暗色主题不再出现大面积
--accent-soft深蓝底。校验通过的目录用成功色 7% 混入表面色的卡片显示,并以.motion-reveal进场。 - 主次:未选择目录时「选择本地检出目录」为主按钮;校验通过后「注册项目」成为主按钮,「选择其他文件夹」降为次按钮,「取消」降为幽灵按钮。页脚与正文同底色,不加分隔线。
- 动效:沿用居中 Dialog 的 240ms 进入(缩放 0.98 + 上移 8px + 淡入)、160ms 退出,遮罩同步淡入淡出。
App.vue把弹窗的开合状态与导航数据分开保存,关闭时内容保留到退出动画结束后才卸载。品牌标记在打开后 60ms 以软弹簧从 0.6 缩放弹入。减少动态效果时缩放与位移归零,时长由全局规则压缩。
在浏览器中验证:亮/暗主题、桌面窗格与 390px 宽度下无横向溢出;退出时 dialog-out 与 overlay-out 播放后卸载,重新打开会清空上次选择的目录。尚未在 Electron 安装版中通过真实协议链接验收。