PrivateGPT Workbench 风格指南:单文件演示 UI 的视觉语言、布局体系与玻璃拟态组件规范
本篇基于 PrivateGPT 仓库 ui/docs/STYLE_GUIDE.md 的风格指南展开,系统讲解 PrivateGPT Workbench 这个单文件演示 UI 的视觉方向、布局体系、玻璃拟态(Frosted Glass)组件规范与交互细节,并结合 ui/index.html 的实际实现印证每一条规范是如何落地的。读完后你将掌握:如何在不依赖任何前端框架的前提下,用单文件 HTML + 原生 CSS 变量构建出一套深色氛围感、可复用、与品牌视觉一致的本地 AI 工作区界面。
定位:它是 PRD 的视觉孪生,而非独立设计系统
风格指南开篇明确了自身的职责边界:它定义 PrivateGPT Workbench 的视觉与 UX 方向,与产品需求文档 ui/docs/PRD.md 互补。指南原文的目标是“在保持实现简单的前提下,让演示器与 PrivateGPT/Zylon 的对外视觉语言对齐”。
从仓库结构看,ui/ 目录的文档分工由 ui/docs/SOURCE_OF_TRUTH.md 明确划分:产品行为归 PRD.md,视觉规则归 STYLE_GUIDE.md,运行时实现只有一份 ui/index.html(约 8000 行,内联全部 HTML/CSS/JS)。这种“一份规范文档 + 一个运行时文件”的组织方式,使得风格指南中的每条规则都能在 ui/index.html 中直接找到对应实现。
参考资产清单
指南要求实现以仓库内图片作为视觉基准,全部位于 ui/references/ 下:
| 参考图 | 文件 | 用途 |
|---|---|---|
| 主聊天布局 | ui/references/primary-chat-layout.png | 侧边栏、聊天布局、消息气泡、composer 位置的主参考 |
| 搜索浮层 | ui/references/search-overlay.png | 模态/搜索浮层、大玻璃面板、标签/芯片、过滤结果列表 |
| 聊天工具 composer | ui/references/chat-tools-composer.png | composer、文件/工具控制、消息密度、聊天气泡处理 |
| Context 知识库 | ui/references/context-knowledge-base.png | Context 行、来源列表、文件徽章、溢出菜单、玻璃列表面 |
此外指南规定:PrivateGPT Logo 必须以内联 SVG 直接嵌入 index.html,使页面运行时不依赖任何外部 Logo 资源——这与其“单文件可运行”的约束一致(仓库中另有 ui/references/logo-privategpt-wordmark-dark-transparent.svg 与 ui/references/logo-privategpt-wordmark-white-transparent.svg 两个 wordmark 资产可供取形)。
设计意图与产品表面
Workbench 应当像一个“打磨精良的本地 AI 工作区”,既不是通用管理后台,也不是营销落地页。界面需要传达四点:本地优先(local-first)的 AI 工具属性、不吓退非技术用户的技术能力、与公共品牌对齐的高级感、以及简洁直接。
产品表面(product surface)被固定为如下结构:
Sidebar
Context
New Chat
Chat list
API Debugger
Settings
GitHub
Not for Production
Main
Context screen
or
API Debugger screen
or
Settings screen
or
Chat screen
在 ui/index.html 中,这一表面由 .app 网格直接落地:grid-template-columns: var(--sidebar) minmax(0, 1fr),全视口高度(100vh),四周边距 16px、间隙 16px。minmax(0, 1fr) 配合各面板的 min-width: 0 是关键——它保证长 URL、长模型名等内容只能截断而不会撑破网格列,这正是指南在 API Debugger 一节中“两个面板都必须 min-width: 0”要求的底层机制。
视觉语言:深色氛围工作区
指南规定使用深色、有氛围感的工作区背景,具体要素包括:
- 深海军蓝、炭灰、黑色为主,辅以柔和琥珀色与微妙的紫/棕色调;
- 覆盖整个视口的径向或混合渐变;
- 背景上叠加细颗粒噪点纹理;
- 毛玻璃面板:模糊 + 浅色描边 + 柔和阴影;
- 白色主文本、灰化次级文本;
- 受 PrivateGPT/Zylon “orb” 启发的蓝/橙/紫小面积渐变点缀。
同时明确列出了禁止项:扁平管理台灰、营销 hero 区块、与功能无关的大装饰卡片、亮白页面背景、过度紫蓝渐变主导、重型组件库观感。
背景实现:变量 + 三层渐变 + 噪点伪元素
指南给出的背景实现是一段可直接引用的 CSS。核心设计令牌定义在 :root:
:root {
--bg: #090b12;
--text: #f7f7fb;
--muted: rgba(255, 255, 255, 0.58);
--muted-strong: rgba(255, 255, 255, 0.74);
--faint: rgba(255, 255, 255, 0.36);
--glass: rgba(255, 255, 255, 0.1);
--glass-soft: rgba(255, 255, 255, 0.07);
--glass-strong: rgba(255, 255, 255, 0.16);
--border: rgba(255, 255, 255, 0.24);
--border-soft: rgba(255, 255, 255, 0.14);
--shadow: 0 24px 80px rgba(0, 0, 0, 0.38);
--danger: #ff9f9f;
--ok: #91e8bd;
--warn: #ffd18a;
--accent: #f0a247;
--blue: #70b7ff;
--radius-xl: 32px;
--radius-lg: 24px;
--radius-md: 16px;
--radius-sm: 10px;
--sidebar: 286px;
}
注意 --sidebar: 286px 这条变量:它不仅是布局参数,还是全应用唯一的侧边栏宽度来源,实现中 .app 网格直接引用它。
背景由三层径向渐变叠加一层线性渐变构成:
body {
background:
radial-gradient(circle at 17% 78%, rgba(36, 125, 190, 0.58), transparent 36%),
radial-gradient(circle at 83% 16%, rgba(212, 148, 63, 0.38), transparent 31%),
radial-gradient(circle at 53% 42%, rgba(92, 50, 116, 0.34), transparent 40%),
linear-gradient(135deg, #081427 0%, #15121c 42%, #130b05 100%);
}
三个径向光斑分别对应视觉语言中“蓝 / 琥珀 / 紫”三种品牌色调,落在视口左下、右上、中心三处,避免任何一色占据主导——这正是“避免紫蓝渐变过度主导”的实现方式。
噪点纹理通过 body::before 固定伪元素实现:0.65px 的白色点阵(background-size: 3px 3px)以 opacity: 0.24 和 mix-blend-mode: overlay 叠在全局之上,pointer-events: none 保证不干扰交互。
在 ui/index.html 的实际实现中,这些令牌被进一步拆分为可被 onboarding“外观自定义”动态覆写的变量(如 --bg-glow-a/b/c、--sidebar-wash-top/bottom、--surface-tint 等,见 ui/index.html 的 :root 定义)。这与 ui/docs/SOURCE_OF_TRUTH.md 中“外观覆写是运行时变量(state.uiAppearance 通过 applyAppearance() 驱动 CSS 自定义属性)”的说明一致:风格指南定义的 :root 是基线值,运行时可以被生成式主题改写,但结构不变。
布局体系
App Shell(应用骨架)
布局以 ui/references/primary-chat-layout.png 为基准,推荐尺寸如下:
- 侧边栏宽度:
286px(--sidebar变量); - 主区聊天内容最大宽度:
860px; - Composer 宽度与聊天内容宽度对齐;
- 全视口高度;
- 除非必要,不设独立顶栏。
指南还要求“背景必须能透过玻璃表面、并从玻璃四周显露出来”——这决定了所有面板背景必须是半透明渐变(后文玻璃表面一节展开),而不能使用不透明填充。
实现印证:ui/index.html 中 .messages 使用 width: min(860px, 100%) 精确落实 860px 上限;同时 flex: 1 加 min-height: 0 使其在网格内弹性伸缩。
侧边栏
侧边栏是一块纵向毛玻璃面板,内容结构固定为:
PrivateGPT logo
Context
New Chat
Chats
Chat title
Chat title
Chat title
API Debugger
Settings
GitHub
Not for Production
规范要点逐条列出:
- Logo 置顶左上角;单文件应用中必须使用内联嵌入 SVG;
- 导航保持紧凑、可读;
- 激活项使用更亮的玻璃态(brighter glass state);
- 聊天行标题超长必须截断;
- 不做项目、文件夹或聊天分组;
- 底部群组顺序严格为:API Debugger 在 Settings 上方,Settings 在 GitHub 仓库组件上方,GitHub 组件在 Not for Production 披露上方;
- 避免让侧边栏看起来像企业级管理菜单;
- 聊天列表用
flex: 1 1 0加min-height: 0占满导航按钮与底部群组之间的全部垂直空间; - 聊天列表使用 CSS
mask-image实现滚动感知的上/下渐隐遮罩,自定义属性为--fade-top-stop/--fade-bot-stop,滚动时由 JS 更新。
实现印证:ui/index.html 中侧边栏 DOM 顺序与上述结构完全一致——.chat-list-wrap(id="chatListWrap")位于导航按钮之后,底部依次是 API Debugger、Settings、GitHub 与 “Not for Production” 行。滚动渐隐的 JS 侧见 ui/index.html 的 updateChatListFade(),它绑定在 #chatListWrap 的 scroll 事件上(ui/index.html)。
主聊天区
聊天视图应当居中且留白充足。消息行为规范:
- 用户消息右对齐,助手消息左对齐;
- 气泡使用半透明玻璃填充;
- 助手消息可带小型渐变 orb/头像,用户消息可带低调头像或标签;
- 引用(citation)以内联、紧凑的上标式标记或小芯片呈现;
- 工具活动以内联低调状态块呈现,而非大卡片;
- 消息列表使用与聊天列表相同的
mask-image滚动渐隐模式。
实现印证:ui/index.html 的 .messages 直接内联了指南的 mask-image 四段式渐变(transparent 0 → black var(--fade-top-stop) → black calc(100% - var(--fade-bot-stop)) → transparent 100%),滚动更新逻辑在 updateMessagesFade()(ui/index.html),并在自动滚到底部时同步调用(ui/index.html)。
Composer(消息输入区)
Composer 以 ui/references/chat-tools-composer.png 为主参考。指南对此节的规定是全篇最细的,核心要求包括:
基础形态
- 双行玻璃输入:textarea 在上,单一工具栏行在下;
- 输入框略高、工具栏控件 32px、标签中等字重,保证主输入“视觉分量足但不变成大卡片”;
- 只复制参考图的视觉处理与交互模式,不得引入 PrivateGPT 未实现的功能、标签、模式或上下文语法;
- composer 控件使用自包含的内联 SVG,避免全局图标水合与尺寸调整导致变形。
左侧工具栏
- 一个合并的“添加/操作”菜单:初始是居中圆形加号按钮,悬停或键盘聚焦时平滑展开显示简短 “Add” 标签;菜单内第一项是文件附加,其后是既有的 PrivateGPT 上下文/工具配置项;
- 可搜索的模型与推理档位(reasoning effort)选择;
- 模型刷新按钮。
动效规范
- 工具栏图标使用克制的悬停动效:刷新旋转、发送上抬、下拉箭头响应悬停/展开态;
- composer 下拉菜单从触发点向外“形变”展开,使用弹簧式缩放 + 圆角过渡,收起时反向缩回触发点;
- 模型 effort 轨道(rail)在主模型列表之后以短横向裁剪过渡出现,模型行与 effort 行带克制的交错入场;过滤时重排行过渡,保证列表变化可读;
- 必须响应
prefers-reduced-motion,关闭 composer 菜单、轨道与行动画。
浮层几何约束
- 浮动 composer 菜单必须把宽度、高度与水平位置夹取(clamp)到当前视口内,内部列表可滚动,但面板本体不允许渲染到窗口之外。
功能语义
- 文件附加行为二选一:若 Code Execution 已启用,上传到当前代码执行会话;否则摄取(ingest)到已配置的 Documents 集合;
- 模型下拉带聚焦搜索框,按显示名过滤已加载模型;
- 模型下拉为两栏布局:左侧可搜索模型,右侧推理档位轨道;档位取值为 None、Low、Medium、High、Max、XHigh,模型不支持的档位禁用;
- 推理档位取代独立的 Thinking composer 按钮,且按聊天(per chat)存储。
Tools 控件
Tools 控件暴露聊天级开关:Documents、Web、Databases、MCP、Skills、Custom Tools;已选上下文项可以紧凑芯片形式展示。
实现印证:ui/index.html 的 composer 区域即 #modelSelectBtn + #modelDropdown 的组合,与 ui/docs/SOURCE_OF_TRUTH.md 中“模型选择器是自定义下拉而非原生 <select>”“推理档位存于 chat.settings.reasoningEffort,请求以 thinking: { enabled, type } 发送”的说明对应。prefers-reduced-motion 的处理在 ui/index.html 直接列出所有需关闭动画的选择器(.model-dropdown.open/.closing、.menu-panel、.effort-panel、.model-option、.effort-option),与指南“尊重 reduced motion”的要求一一对应。
Context 屏幕:像来源管理器,而不是设置页
Context 屏幕定义助手可以访问什么,外观应当像来源管理器而非设置页。六个分区固定为:
Documents
Databases
Web
MCP
Skills
Custom Tools
视觉处理以 ui/references/context-knowledge-base.png 为参考:玻璃列表行、文件/来源图标、类型徽章、状态元数据、溢出菜单、必要的嵌套/分组行。
各分区的行字段规范:
Documents — 每行显示:图标、名称、所属集合(Collection)、文件类型徽章(如 PDF、DOCX、CSV、HTML)、状态(如 Indexed、Processing、Failed)、溢出菜单(Preview / Search / Delete)。特别强调:Collection 字段属于 Settings 而非 Documents 面板,因为它全局作用于所有文档操作。
Databases — 每行显示:数据库图标、友好名称、主机或简短连接标签、可选的 schema/表元数据、溢出菜单(Edit / Test / Delete)。
Web、MCP、Skills、Custom Tools — 复用同一套行/卡片语言:名称、描述或提供方、配置摘要、溢出操作。两条边界性规定:
- Web 分区仅提供信息展示。Workbench 不收集任何 Web 提供方凭据——Web 提供方与 API Key 配置属于 PrivateGPT 后端(后端配置见 settings.yaml),UI 内只读说明;
- Custom Tools 不得藏在 “Advanced” 标签之后,它是一等公民的 Context 分区。
从 PRD(ui/docs/PRD.md)侧可交叉印证 Collection 的“单集合”约束:Workbench 只有一个活动集合,state.context.documents.defaultCollection 是文档摄取、列表、删除、搜索与聊天请求唯一的全局集合指针,实现中该值默认 pgpt_collection(ui/index.html)。
Settings 屏幕:连接层配置的唯一归属
Settings 拥有 Workbench 级别的连接配置,清单固定为:
- PrivateGPT API 基础 URL;
- 可选 HTTP Basic 认证(用户名与密码字段并排展示);
- 可选系统提示词;
- 可选工作区指令(workspace instructions);
- Use citations 开关;
- Collection —— 用于所有文档操作与聊天请求的活动文档集合名。指南再次强调它属于 Settings 而不属于 Documents 面板;
- 外观控制:品牌文案、色板、可选的可见分区;
- 重跑 onboarding 的控制项;
- Test API 与 Save 按钮;
- 清除本地数据(Clear local data)。
实现印证:ui/index.html 中 Settings 页确实并列放置了 “Run onboarding” 与 “Test API” 两个 ghost-button,ui/index.html 的集合输入框(id="defaultCollection",占位符 pgpt_collection)附带说明文字“所有 Documents 与 Skills 操作使用该集合;文档启用的聊天在此集合中搜索 artifacts”,并提示“某些部署按 bearer token 限制允许的集合 ID”——这正是指南将 Collection 上收至 Settings 的原因。
Onboarding 覆盖层:引导式配置单,不是企业向导
首次运行 onboarding 应当感觉像“引导式配置单(guided setup sheet)”,而不是企业后台向导。规范:
- 作为大号玻璃覆盖层浮在现有应用骨架之上,让用户感到自己在配置的是真实工作区;
- 第 1 步强调清晰与信心:URL、认证、集合,然后是一个简单检查清单,显示 models、collection、skills 三项是否响应;
- 第 2 步更轻、可跳过:一侧是提示词驱动的生成器 + 可编辑结果表单,另一侧是实时预览磁贴;
- 可选自定义步骤必须与 Settings 使用同一套视觉语言,让用户理解两者写入同一组变量;
- GitHub / Zylon 引用属于演示身份的一部分,不允许通过外观自定义移除。
从源码结构看,onboarding 是有状态的:state.onboarding 控制首跑覆盖层的显隐、当前步骤与最近一次实时校验结果,只要 state.onboarding.completed !== true 就会展示覆盖层(见 ui/docs/SOURCE_OF_TRUTH.md 的实现要点)。
API Debugger:会话级、实时、易失
API Debugger 作为侧边栏目的地,位于 Settings 上方。它有三个硬性属性:会话级(session-level)、仅实时(live-only)、易失(ephemeral)。
推荐布局为“时间线列表 | 事件详情面板”(Timeline list | Event detail panel),使用与全局一致的玻璃风格,但密度更高、更偏技术感。Debugger 必须展示:API 请求、API 响应、错误、脱敏后的请求头。
两条实现级约束:
- Debugger 事件不得跨页面重载持久化(与 PRD 的 “Do not store debugger data” 呼应);
- 时间线面板与详情面板都必须设置
min-width: 0,让长 URL 被截断而非撑破面板。
所有请求经由 ui/index.html 的 apiFetch() 统一封装,负责拼接基础 URL、设置请求头、记录耗时、解析响应,并把请求/响应/错误写入会话级 Debugger 缓冲——这也是“Chat、Context、Settings 三处的调用都能出现在 Debugger 中”这一 PRD 要求的落点。
披露组件:Not for Production 与 GitHub Widget
Not for Production 披露:侧边栏在 GitHub 组件下方提供紧凑的 Not for Production 按钮。视觉处理:与 GitHub 组件相同的侧边栏行节奏、轻微暖色/危险色调(读起来是重要披露而非报错)、信息图标加文字标签。点击后打开玻璃风格模态,标题固定为:
This demonstrator is not intended for Production use
正文保持简短,用四条合并要点覆盖:浏览器 localStorage 不是安全密钥存储、无访问控制、Debugger 数据可见、自定义工具在浏览器中执行。结尾附 Zylon 与演示预约入口链接。
GitHub 组件:侧边栏在 Settings 下方的可点击组件,要求链接到 PrivateGPT 仓库、显示 GitHub 图标、标签、星形图标与(可获取时的)实时 star 数;star 获取失败时组件必须保持可用,降级为中性 Stars 标签——这是指南中少见的显式降级要求,保证了静态文件离线打开时组件不坏。
URL Hash 导航:刷新可恢复、前后可达
应用使用 hash 导航,刷新后恢复当前视图。Hash 格式规范如下:
| Hash | 视图 |
|---|---|
#context/documents | Context — Documents 标签 |
#context/databases | Context — Databases 标签 |
#context/web | Context — Web 标签 |
#context/mcp | Context — MCP 标签 |
#context/skills | Context — Skills 标签 |
#context/customTools | Context — Custom Tools 标签 |
#settings | Settings 屏幕 |
#apiDebugger | API Debugger 屏幕 |
#chat/{chatId} | 按 ID 定位的聊天 |
工作机制:syncHash() 在每次 render() 调用末尾执行 history.replaceState;restoreFromHash() 在启动时(首次渲染前)与 hashchange 事件时运行,以支持浏览器前进/后退。
实现印证:ui/index.html 中 syncHash() 与 restoreFromHash() 两个函数即规范所指;ui/index.html 注册了 hashchange 监听(回调中 restoreFromHash() 后重新 render()),ui/index.html 在脚本尾部调用一次 restoreFromHash() 完成启动恢复。syncHash() 也在每次 render() 末尾(ui/index.html)与上下文标签切换(ui/index.html)时被调用,与指南描述完全吻合。
组件与 CSS 规范
玻璃表面:三级模糊体系
全篇最核心的视觉资产是“一套玻璃处理、三档强度”。
第一档:共享玻璃组。 所有主面板通过一个共享 CSS 选择器组获得同一玻璃质感:
.settings-card,
.context-panel,
.debug-panel,
.composer,
.message-bubble,
.menu-panel,
.model-dropdown,
.modal-card {
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.055));
border: 1px solid var(--border);
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.28);
backdrop-filter: blur(22px);
-webkit-backdrop-filter: blur(22px);
}
第二档:浮动面板加浓。 模态卡片、工具菜单、模型下拉这三个浮动面板用“更轻的半透明白 + 更强模糊 + 底部更重”的覆写:
.modal-card,
.menu-panel,
.model-dropdown {
background: linear-gradient(to bottom,
rgba(255, 255, 255, 0.14) 0%,
rgba(255, 255, 255, 0.26) 100%
);
backdrop-filter: blur(72px) saturate(1.6);
-webkit-backdrop-filter: blur(72px) saturate(1.6);
}
指南解释了参数意图:白色玻璃色调保持轻(半透明而非不透明);blur(72px) 足以让背景失焦、文本可读;渐变底部更重,提供视觉“压地感(grounding)”。
第三档:模态背景中等模糊。
.modal-backdrop {
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
}
三档分别对应:常驻面板(22px)→ 浮动面板(72px + 1.6 饱和)→ 遮罩(14px)。在 ui/index.html 中可逐条对上:共享组在 ui/index.html 原样存在,浮动覆写在 ui/index.html,模态背景在 ui/index.html。实现中还有一个指南未写明但值得注意的工程细节:注释明确说明把 backdrop-filter 从 .composer 上移除(ui/index.html),原因是 backdrop-filter 会创建堆叠上下文,导致内部下拉菜单“逃逸”失效——这是对“浮动面板必须叠在 composer 之上”这一视觉约束的必要修复。
模型选择器
用自定义玻璃下拉替换原生 <select>,规范要点:
- 一个
ghost-button,显示 CPU 图标、当前模型名(截断)、带动画的箭头(chevron); - 点击打开定位在 composer 上方的
.model-dropdown面板; - 下拉以
.model-option行列出所有模型,当前选中项带勾选标记; - 点击外部或按 Escape 关闭;
.model-dropdown属于共享玻璃组,并享受更强磨砂覆写。
实现印证:ui/index.html 中 #modelSelectBtn 与 #modelDropdown 的组合即此结构;ui/docs/SOURCE_OF_TRUTH.md 补充了状态细节——选择模型或档位时就地更新已打开的面板 DOM,保留搜索词与滚动位置,仅从触发器、Escape 或外部点击关闭。
工具菜单
Tools 弹出(.menu-panel)按分区分组:
Knowledge
Documents [toggle]
Web [toggle]
Databases [group title]
item [toggle]
MCP [group title]
...
Skills [group title]
...
Custom Tools [group title]
...
每个分组使用 .menu-group 与 .menu-group-title(小号大写标签)。菜单内切换行无边框、仅悬停高亮,以此区别于 Settings 卡片中带边框的切换行——用同一开关组件做出两种语境密度,是“组件复用而不复用观感”的典型手法。
切换开关(Toggle Switches)
全部 input[type="checkbox"] 都样式化为自定义胶囊开关,无任何原生外观:
- 胶囊轨道:
34 × 20px,圆角; - 滑块(knob):
12px圆,垂直居中; - 关闭态:灰白轨道与滑块;
- 开启态:蓝色调轨道(
rgba(80, 150, 255, 0.32))加亮滑块(rgba(150, 205, 255, 0.96)); - 滑块过渡使用
left属性(不是transform: translateX)。
Thinking 按钮
Composer 中的扩展思考(Extended Thinking)开关使用:
- 一个带
zap图标与 “Thinking” 标签的ghost-button; - 非激活态:标准幽灵按钮外观,悬停带紫色提示;
- 激活态:紫色描边、紫色调背景、柔和光晕、紫色调图标。
.thinking-chip.active {
border-color: rgba(130, 80, 230, 0.55);
background: rgba(100, 55, 200, 0.18);
color: rgba(200, 165, 255, 0.95);
box-shadow: 0 0 14px rgba(120, 70, 220, 0.18);
}
实现印证:ui/index.html 的 .thinking-chip 与 .thinking-chip.active 规则与之一致(含激活态图标着色规则);按 PRD 约定,独立 Thinking 按钮已被模型下拉中的推理档位取代,故激活类在实现中保留但按钮节点可用 hidden 属性移除([hidden] 规则见 ui/index.html)。
按钮与芯片
按钮和芯片应“像玻璃环境的一部分”:细描边、半透明填充、悬停略亮、明确的激活态(scale(0.97) 按压反馈)、必要时配图标。指南列出了应有图标的控件清单:发送、搜索、文件/集合、工具、数据库、更多菜单、删除、刷新/测试、CPU(模型选择器)、下箭头(下拉指示)、勾选(下拉选中项)、Zap(思考开关)。若不使用图标库,允许内联 SVG,但要保持极简——这与 Composer 一节“自包含内联 SVG”的要求同源:Workbench 没有任何图标库依赖。
菜单与浮层
以 ui/references/search-overlay.png 为参考,要求:
- 玻璃浮层面板,使用更强的磨砂处理;
- 强描边、柔和阴影;
- 搜索类浮层使用大号易读输入框;
- 需要时配紧凑胶囊过滤项;
- 结果以干净行呈现。
.menu-panel 与 .model-dropdown 与 .modal-card 共享同一套“玻璃 + 强磨砂”覆写,保证所有浮层观感一致。
滚动渐隐(Scroll Fades)
任何可能溢出的滚动列表都要加上下滚动感知的渐隐遮罩:
.scrollable-wrap {
--fade-top-stop: 0px;
--fade-bot-stop: 0px;
mask-image: linear-gradient(
to bottom,
transparent 0,
black var(--fade-top-stop),
black calc(100% - var(--fade-bot-stop)),
transparent 100%
);
}
--fade-top-stop 与 --fade-bot-stop 在滚动时由 JavaScript 更新。适用对象:
.chat-list-wrap—— 侧边栏聊天列表(22px 渐隐深度);.messages—— 主消息列表(28px 渐隐深度)。
实现印证:两套规则分别位于 ui/index.html(.chat-list-wrap,含 transition: mask-image 150ms ease 的平滑过渡)与 ui/index.html(.messages);两个 JS 更新器 updateMessagesFade() / updateChatListFade()(ui/index.html)均绑定 passive: true 的 scroll 监听并在启动时各执行一次,与 ui/docs/SOURCE_OF_TRUTH.md “滚动渐隐”条目所述完全一致。
排版规范
使用现代无衬线字体栈:
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
排版准则:
- 主文本:高对比白色;
- 次级文本:灰化白/灰;
- 避免超大 hero 字号;
- 聊天文本要保证舒适可读;Context/Debugger 文本可以更密;
- 不使用负字距(letter spacing);
- 字号不随视口宽度缩放。
实现印证:该字体栈在 ui/index.html 中被存为 --font 变量并由 body 统一引用,所有控件通过 font: inherit 继承,不存在第二套字体声明。
响应式行为
v1 的首要目标是桌面端。最小行为要求:
- 窄宽度下侧边栏可折叠或变为浮层;
- 聊天 composer 保持可用;
- 文本不得溢出按钮、芯片或行;
- Context 行应换行元数据,而不是裁掉关键标签。
从源码结构看,当前实现对“文本不溢出”这一条的兜底手段是网格列上的 minmax(0, 1fr) 与面板级 min-width: 0 / overflow: hidden + text-overflow: ellipsis(例如 Debugger 面板内元素,ui/index.html)。
实现约束与验证方式
视觉系统不得要求前端框架。指南给出的首选实现形态:
- 单一静态
./index.html; - 原生 CSS 变量;
- 小型可复用 CSS 类;
- 重复的 HTML 模板可接受;
- 不要求抽取设计系统。
最终目标是支撑 PRD 的简洁性要求:足以支撑面向公众的演示的打磨度,但不按长期产品前端去架构。
ui/README.md 给出了维护这套单文件实现时的验证手段:任何对 ui/index.html 的改动后,可用 Node 一行脚本确认内联脚本可解析:
node -e "const fs=require('fs'); const html=fs.readFileSync('./ui/index.html','utf8'); const m=html.match(/<script>([\s\S]*)<\/script>/); if(!m) throw new Error('script tag not found'); new Function(m[1]); console.log('script ok')"
同时 README 列出了 ui/ 目录的分工与四条工作规则(实现只进 index.html、产品/设计/架构指导只进 docs/、视觉参考图只进 references/、文档与实现必须随行为变更同步更新)——这正是本篇所依据的 STYLE_GUIDE.md 得以与 ui/index.html 保持逐条可对照的治理机制。
小结
PrivateGPT Workbench 的风格指南本质上是一份“约束密集型”设计契约:它用固定的产品表面、三级玻璃模糊体系、精确到像素的尺寸(286px 侧边栏、860px 聊天宽、34×20px 开关轨道)、明确的禁止清单与 prefers-reduced-motion 兜底,把一个“没有框架、只有一个 HTML 文件”的演示 UI 约束成了与品牌视觉一致的完整工作区。对照 ui/index.html 的实现可以看到,指南中每一条 CSS 片段、每一处布局数值与交互降级要求都能在单文件中找到对应规则与行号级落点,这也是该仓库“文档即规范、实现即证据”的文档化方式的价值所在。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



