- 数据库客户端
- 后端
- 前端
- CLI
- 桌面应用
- 数据可视化
- AI 应用
【免费下载链接】whodb
Where data access meets operational intelligence
本篇基于 whodb 仓库中 impeccable 设计技能包内的参考文档harden.md(.agents/skills/impeccable/reference/harden.md)展开。该文档定义了前端界面在交付前必须经历的“硬化”流程:用极端输入、错误场景和国际化压力测试来发现弱点,再系统性地在文本溢出、i18n、错误处理、边界条件、输入校验、可访问性与性能等维度加固。读完后你将掌握一套完整的界面加固检查清单,并能从 whodb 前端源码中找到每一条原则的真实落地证据——例如 GraphQL 请求的有限重试策略、基于 CLDR 的复数规则本地化、以及把数据库原始报错翻译成多语言“可读提示”的ErrorState组件。
一、硬化在整个设计工作流中的位置
harden.md不是一个孤立的检查单,它是 impeccable 技能(入口为.agents/skills/impeccable/SKILL.md)中 18 个子命令之一。在SKILL.md的命令表里,harden [target]被归类为Refine(精化)类命令,与polish、bolder、distill等同属“已有设计之上做改进”的类别,其描述是 “Production-ready: errors, i18n, edge cases”(面向生产就绪:错误、国际化、边界条件)。
文档给出的核心理念一句话即可概括:
Designs that only work with perfect data aren't production-ready. Harden the interface against the inputs, errors, languages, and network conditions that real users will throw at it.
即“只在完美数据下才正常的设计不具备生产能力”,必须针对真实用户会抛出的输入、错误、语言和网络条件去加固。文档还特别强调(原文标注 CRITICAL):不要假设完美数据,要对现实做防御。在 whodb 这样连接真实数据库(PostgreSQL、MySQL、MongoDB、Redis 等)的工具型产品中,这条原则尤其重要——用户查询失败、连接超时、表结构千差万别都是日常,前端必须为“坏情况”设计得和“好情况”一样认真。
文档最后给出了工作流衔接:当边界条件全部覆盖后,“hand off to$impeccable polishfor the final pass”——硬化完成后交给polish.md做交付前的最终质量巡检。这形成了一个清晰的 Refine 链条:先harden补足错误/边界/国际化,再polish统一视觉与流程。
二、评估硬化需求:三类压力测试清单
文档的第一部分是 “Assess Hardening Needs”,要求先识别弱点与边缘情况,方法是对界面施压。这三类压力测试构成了整个流程的输入:
1. 极端输入测试
- 非常长的文本(姓名、描述、标题)
- 非常短的文本(空、单字符)
- 特殊字符(emoji、RTL 文本、带重音的字母)
- 大数值(百万、十亿)
- 大量条目(1000+ 列表项、50+ 选项)
- 无数据(空状态)
2. 错误场景测试
- 网络故障(离线、慢速、超时)
- API 错误(400、401、403、404、500)
- 校验错误
- 权限错误
- 限流(Rate limiting)
- 并发操作
3. 国际化测试
- 更长的译文(德语通常比英文长 30% 左右)
- RTL 语言(阿拉伯语、希伯来语)
- 字符集(中文、日文、韩文、emoji)
- 日期/时间格式
- 数字格式(
1,000与1.000两种千分位约定) - 货币符号
这三类测试在 whodb 中都有对应的真实场景:数据库表名和列名可以是任意长度的用户自定义字符串;查询失败会返回各种数据库引擎的原始报错;而 whodb 前端支持 50 多种语言(见下文 i18n 章节),阿拉伯语、希伯来语(iw_IL)等 RTL 语言也在其列——这意味着“国际化测试”不是理论清单,而是这个产品必须通过的日常验收项。
三、硬化维度一:文本溢出与换行
文档给出了三组可直接复用的 CSS 模式,这是全文最“可执行”的部分:
长文本处理:
/* 单行省略号 */ .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 多行截断(clamp) */ .line-clamp { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } /* 允许换行 */ .wrap { word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; }Flex/Grid 溢出:
/* 防止 flex 子项撑破容器 */ .flex-item { min-width: 0; /* 允许收缩到比内容更小 */ overflow: hidden; } /* 防止 grid 子项撑破容器 */ .grid-item { min-width: 0; min-height: 0; }min-width: 0是 flex/grid 布局中最容易被忽略的一条:flex 子项默认min-width: auto,内容(尤其是长表名、长 SQL 片段、长错误信息)会强行撑破布局。对 whodb 这类展示数据库内容的工具,这一点几乎是必踩的坑。
响应式字号:
- 使用
clamp()做流式排版; - 设定可读的最小字号:移动端正文 16px(14px 仅用于真正的次级文本)——文档解释了原因:iOS Safari 会对小于 16px 的聚焦输入框强制放大(force-zoom),破坏表单布局;
- 测试 200% 缩放;
- 确保容器随文本扩展。
whodb 前端确实大量使用了truncate类与 line-clamp 手法,可在frontend/src/pages/storage-unit/storage-unit.tsx、frontend/src/components/sidebar/sidebar.tsx、frontend/src/pages/chat/chat-history-sidebar.tsx等文件中检索到,覆盖了表格单元、侧边栏条目、会话历史标题这类典型的“长数据库对象名”场景。
四、硬化维度二:国际化(i18n)
文档在 i18n 上给出了五个子主题,并各附代码示例:
文本扩容
- 为译文预留 30–40% 的空间预算;
- 使用能随内容自适应的 flexbox/grid;
- 用最长语言(通常是德语)测试;
- 避免给文本容器固定宽度。
// ❌ 错误:假设是短英文文本 <button className="w-24">Submit</button> // ✅ 正确:宽度随内容自适应 <button className="px-4 py-2">Submit</button>RTL(从右到左)支持
/* 使用逻辑属性(logical properties) */ margin-inline-start: 1rem; /* 而非 margin-left */ padding-inline: 1rem; /* 而非 padding-left/right */ border-inline-end: 1px solid; /* 而非 border-right */ /* 或用 dir 属性 */ [dir="rtl"] .arrow { transform: scaleX(-1); }字符集支持
- 全程使用 UTF-8 编码;
- 用 CJK(中日韩)字符测试;
- 用 emoji 测试(它们可能占 2–4 字节);
- 处理不同文字系统(拉丁、西里尔、阿拉伯等)。
日期/时间格式
// ✅ 用 Intl API 做正确的格式 new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024 new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024 new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1234.56); // $1,234.56复数化
// ❌ 错误:假设英语式复数 `${count} item${count !== 1 ? 's' : ''}` // ✅ 正确:使用正确的 i18n 库/规则 t('items', { count }) // 处理复杂的复数规则whodb 源码中的落地证据
whodb 前端的国际化体系与文档的每一条建议高度对应,是这套清单的现实注脚:
语言覆盖面:
frontend/src/utils/languages.ts定义了 50 多个SupportedLanguage联合类型,包括ar_AE(阿拉伯语)、iw_IL(希伯来语,即文档点名的 RTL 测试语言)、ja_JP/ko_KR/zh_CN/zh_TW(CJK)、de_DE(德语,即文档点名的“最长译文”语言)。RTL 与 CJK 不是文档的假想测试项,而是产品的一等公民。CLDR 复数规则:
frontend/src/utils/i18n.ts中的getPluralCategory函数直接基于Intl.PluralRules实现,返回"zero" | "one" | "two" | "few" | "many" | "other"六种 CLDR 复数类别,并对每个 locale 缓存PluralRules实例(源码注释说明构造时会加载 CLDR 数据,每个 locale 一个实例即可)。这正是文档中 “Use proper i18n library / Handles complex plural rules” 一行的具体实现——注意俄语(ru_RU,whodb 支持的语言)有 few/many 复数类别,简单的count !== 1 ? 's' : ''在此必然出错。复数键约定:
.agents/rules/localization.md规定复数化使用_one、_other、_few、_many、_zero后缀加count参数——与文档的建议完全一致,并且把规则落成了团队强制的编码规范。日期序列化防御:
frontend/src/store/index.ts在应用启动时检查 localStorage 中持久化的 scratchpad 数据,一旦发现历史条目里存在非法日期字符串就整段清除(源码注释:“Clear any corrupted scratchpad data on startup to prevent date serialization issues”)。这正是“边界条件 → 存储层数据损坏”场景的一次防御:持久化数据不是完美数据,前端必须容忍它坏掉。一个真实的“未硬化”痕迹:
.agents/rules/localization.md明确写明“非英语的 locale 条目不要自动添加,翻译是人工维护步骤”,翻译由dev/translate/下的工具链手工执行。翻看frontend/src/locales/components/error-state.yaml可以看到,部分语言(如th_TH、ur_PK、uz_UZ、kn_IN)的hint.syntaxError条目仍保留英文原文——这是“人工维护翻译”流程中真实存在的缺口,恰好印证了文档中“Test with CJK characters / character sets”这类检查为什么不能只写在清单上,而要在验证阶段真正执行。
五、硬化维度三:错误处理
文档把错误处理拆成四块:
网络错误:
- 显示清晰的错误消息;
- 提供重试按钮;
- 解释发生了什么;
- 在适用时提供离线模式;
- 处理超时场景。
// 带恢复路径的错误状态 {error && ( <ErrorMessage> <p>Failed to load data. {error.message}</p> <button onClick={retry}>Try again</button> </ErrorMessage> )}表单校验错误:
- 错误显示在字段附近(inline);
- 消息清晰具体;
- 给出修改建议;
- 不要不必要地阻塞提交;
- 出错时保留用户已输入的内容。
API 错误:按状态码分别处理——400 显示校验错误、401 跳转登录、403 显示权限错误、404 显示未找到状态、429 显示限流消息、500 显示通用错误并提供支持入口。
优雅降级:核心功能不依赖 JavaScript 也能工作、图片有 alt 文本、渐进增强、对不支持的特性提供回退。
whodb 源码中的落地证据
1. 有限重试:只重试“真正瞬时”的失败。frontend/src/config/graphql-client.ts中的RetryLink配置是这份文档“网络错误”章节的严谨实现:
const transientStatusCodes = new Set([502, 503]); const maxRequestAttempts = 3; const retryLink = new RetryLink({ delay: {initial: 500, max: 4000, jitter: true}, attempts: (attempt, operation, error) => { if (attempt >= maxRequestAttempts) return false; // 只重试 query,mutation 永不重试 // GraphQL 层错误不视为瞬时错误 // 只有网络故障或 502/503 才重试 }, });源码注释里有两处值得注意的“硬化思维”:其一,504 被刻意排除在瞬时状态码之外——“server's own request timeout answers with 504, and replaying a query that already took too long only adds load”(重放一个已经超时的查询只会增加负载);其二,每次重试都会通过trackFrontendIntent上报分析事件,让“用户从未感知到的瞬时失败”依然可观测。这比文档给出的通用建议更进一步:重试不是越多越好,而是要精确定义“什么是可重试的”。
2. 把数据库原始报错翻译成“可读提示”。frontend/src/components/error-state.tsx是文档“API 错误:按状态码/类别分别处理”原则在数据库工具里的独特变体——它要处理的不是 HTTP 状态码,而是各数据库引擎的原始错误文本。parseError函数用正则识别 10 类常见错误(列不存在、表不存在、语法错误、权限被拒、唯一键冲突、非空约束、外键约束、超时、连接拒绝、认证失败),命中时同时展示“人话解释”(经 i18n 翻译)和原始错误文本:
if (/column .+ does not exist/i.test(message) || /unknown column/i.test(message)) { return { explanation: t('hint.columnNotFound'), raw: message }; } // ... 共 10 类组件用role="alert"向辅助技术播报(可访问性要求),并提供一个悬浮出现的CopyButton把原始错误复制到剪贴板——这解决了“解释要清晰”与“原始信息不能丢”的矛盾。而配套的frontend/src/locales/components/error-state.yaml把 10 条提示翻译到了 50 多个语言区,正是文档 i18n 章节“为每种语言提供错误消息”的大规模实践。
3. 空状态与加载状态。文档“Edge Cases”章节要求的 empty/loading states 在 whodb 的表格组件frontend/src/components/table.tsx中有集中实现(其内部对EmptyState的使用与大量aria-label标注可在源码中检索到)。
六、硬化维度四:边界条件
文档列出的六类边界条件,每类都给出了明确的工程做法:
| 边界条件 | 文档要求 |
|---|---|
| 空状态 | 列表为空、搜索无结果、无通知、无数据可显示时,都要提供清晰的下一步动作 |
| 加载状态 | 区分首次加载、翻页加载、刷新;说明“正在加载什么”;长操作给出时间估计 |
| 大数据集 | 分页或虚拟滚动;提供搜索/过滤;性能优化;不要一次加载 10,000 条 |
| 并发操作 | 防止重复提交(loading 时禁用按钮);处理竞态;乐观更新带回滚;冲突解决 |
| 权限状态 | 无查看权限、无编辑权限、只读模式,并清楚解释“为什么” |
| 浏览器兼容 | 现代特性的 polyfill;不支持的 CSS 的回退;特性检测(而非浏览器检测);在目标浏览器上测试 |
对 whodb 而言,“大数据集”这一条直接对应其核心场景:一个用户表的行数可能是几十万行,数据视图必须分页加载而不是一次取回。文档“不要把整个界面因一个组件报错而阻塞”(NEVER 列表末条)也与 whodb 组件化架构(每个视图独立渲染错误状态)的设计取向一致。
七、硬化维度五:输入校验与清理
文档将校验分为三层:
客户端校验:必填字段、格式校验(邮箱/电话/URL)、长度上限、模式匹配、自定义规则。
服务端校验(永远要做):不要只信任客户端;对所有输入做校验和清理;防注入;限流。
约束声明(HTML 层):
<!-- 声明清晰的约束 --> <input type="text" maxlength="100" pattern="[A-Za-z0-9]+" required aria-describedby="username-hint" /> <small id="username-hint"> Letters and numbers only, up to 100 characters </small>注意这个示例同时体现了可访问性要求:aria-describedby把约束提示关联到了辅助技术。whodb 前端对aria-describedby、aria-label的使用遍布登录页(frontend/src/pages/auth/login.tsx)、数据浏览器(frontend/src/pages/storage-unit/explore-storage-unit-where-condition.tsx等),与这一原则吻合;而后端则有独立的 SQL 安全层(仓库文档.agents/docs/sql-security.md与core/src/sqlguard/目录描述了针对数据库输入的防护),正好对应“服务端永远要校验”这一条在 whodb 中的分工。
八、硬化维度六:可访问性韧性与性能韧性
可访问性韧性
- 键盘导航:所有功能可用键盘触达、逻辑的 Tab 顺序、模态框内的焦点管理、长内容的跳转链接;
- 屏幕阅读器:正确的 ARIA 标签、动态变化的播报(live regions)、描述性 alt 文本、语义化 HTML;
- 高对比度模式:在 Windows 高对比度模式下测试;不要只依赖颜色;提供替代视觉线索。
whodb 仓库为这条清单配置了自动化设施:frontend/e2e/playwright.a11y.config.mjs是专门的 a11y 测试配置,frontend/e2e/Dockerfile.a11y-report用于生成 a11y 报告,对应文档“Automated testing”一节中 “Accessibility tests (axe, WAVE)” 的要求。
性能韧性
- 慢网络:渐进式图片加载、骨架屏、乐观 UI 更新、离线支持(service worker);
- 内存泄漏:清理事件监听器、取消订阅、清除定时器、组件卸载时中止进行中的请求;
- 节流与防抖:
// 防抖搜索输入 const debouncedSearch = debounce(handleSearch, 300); // 节流滚动处理 const throttledScroll = throttle(handleScroll, 100);whodb 的持久化层也体现了“高频写入的节流”思想:frontend/src/store/index.ts中对houdini、scratchpad、exploreConditions三个 reducer 的persistReducer都配置了throttle: PERSIST_THROTTLE,避免用户快速敲入 SQL 时每个 keystroke 都触发 localStorage 写入——这是 debounce/throttle 思想在存储层的直接应用。
九、测试策略与验证清单
手动测试
用极端数据测试(极长、极短、空);用不同语言测试;离线测试;慢网络(节流到 3G);屏幕阅读器;仅键盘导航;旧浏览器。
自动化测试
边界条件的单元测试;错误场景的集成测试;关键路径的 E2E 测试;视觉回归测试;可访问性测试(axe、WAVE)。whodb 的测试基础设施与此对应:Playwright 常规 E2E(frontend/e2e/playwright.config.mjs)、a11y 专用配置与截图配置(playwright.a11y.config.mjs、playwright.screenshot.config.mjs,后者服务于视觉回归)、CLI 侧的 e2e 套件(cli/e2e/)。
验证硬化是否完成
文档给出的最终验收清单(Verify Hardening):
- 长文本:尝试 100+ 字符的姓名;
- Emoji:在所有文本字段中使用 emoji;
- RTL:用阿拉伯语或希伯来语测试;
- CJK:用中日韩文测试;
- 网络问题:断开网络、节流连接;
- 大数据集:用 1000+ 条目测试;
- 并发操作:快速连点提交 10 次;
- 错误:强制制造 API 错误,测试所有错误状态;
- 空:清空所有数据,测试空状态。
全部覆盖之后,才把目标移交给polish(见.agents/skills/impeccable/reference/polish.md,其第 4 节“Interaction and state”明确要求验证“long, missing, localized, offline, slow, and permission-limited content”,与本文清单首尾呼应)。
十、NEVER 清单:十条禁令
文档最后用 “NEVER” 给出了十条禁令,值得原样保留作为代码评审的检查项:
- 不要假设完美输入(一切都要校验);
- 不要忽视国际化(为全球设计);
- 不要留泛泛的错误消息(“Error occurred”);
- 不要忘记离线场景;
- 不要只信任客户端校验;
- 不要给文本用固定宽度;
- 不要假设英语长度的文本;
- 不要在一个组件报错时阻塞整个界面。
whodb 仓库自身的配套工程实践也为这份清单提供了“如何规模化执行”的参照:dev/translate/目录提供了翻译占位符检查(check_placeholders.py)、检测(detect.py)与修复(fix_placeholders.py)脚本,保证 50 多个语言的{placeholder}插值不被翻译过程破坏——这本身就是“国际化不能只靠翻译一遍就结束”的工程化体现。
总结
harden.md的价值在于把“界面要健壮”这句空话拆成了可执行、可验收的三个层次:先按“极端输入 / 错误场景 / 国际化”三类清单施压找出弱点;再按“文本溢出、i18n、错误处理、边界条件、输入校验、可访问性、性能”七个维度系统性修复;最后用九项验证清单与十条 NEVER 禁令确认交付。而 whodb 前端源码——从graphql-client.ts中精确界定瞬时错误的RetryLink,到error-state.tsx把 10 类数据库报错翻译成 50 种语言的可读提示,再到i18n.ts基于Intl.PluralRules的 CLDR 复数实现——为这份清单提供了完整的真实注脚:生产级前端不是“正常路径写得多好”,而是对坏路径的准备做得多细。
- 数据库客户端
- 后端
- 前端
- CLI
- 桌面应用
- 数据可视化
- AI 应用
【免费下载链接】whodb
Where data access meets operational intelligence
相关推荐
ccg-workflow /harden 实战指南:错误处理、国际化与边界条件驱动的界面生产级加固
ccg workflow /harden 实战指南:错误处理、国际化与边界条件驱动的界面生产级加固 导读 : harden 是 ccg workflow 的 i
用 impeccable 的 harden 流程打磨生产级前端:输入、错误、i18n 与边界场景的系统性加固
用 impeccable 的 harden 流程打磨生产级前端:输入、错误、i18n 与边界场景的系统性加固 本篇技术指南围绕 Impeccable 设计语言中
AI 技能前端CLIdsh-pluginImpeccable harden 命令实战:一套面向生产环境的 UI 加固手册——文本溢出、i18n、错误态与边界条件
Impeccable harden 命令实战:一套面向生产环境的 UI 加固手册——文本溢出、i18n、错误态与边界条件 impeccable 是一个为 AI
AI 技能前端CLIdsh-plugin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考