十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Friend 品牌 UI 不变量 INV-UI-1 全解:禁止紫色与白/中性色强调体系(No-Purple 无增长棘轮)

Friend 品牌 UI 不变量 INV-UI-1 全解:禁止紫色与白/中性色强调体系(No-Purple 无增长棘轮) Friend 品牌 UI 不变量 INV-UI-1 全解禁止紫色与白/中性色强调体系No-Purple 无增长棘轮【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend导读本文围绕 Friend 开源仓库的product/invariants/brand-ui.md这一条锁定locked产品不变量INV-UI-1展开系统讲解该项目的品牌视觉红线界面中一律不使用紫色主操作与强调色统一采用白色/中性色处理。你将掌握这条不变量在桌面端 SwiftUI、Flutter App 与 Web 端的具体落地范围以及仓库如何用.github/scripts/check_brand_ui.py的**无增长棘轮no-increase ratchet**在 CI 中自动拦截新增紫色包括其检测模式、HSV 色相判定算法、允许列表与 PR 协作规则。一、不变量是什么品牌红线的定义与状态在 brand-ui.md 中INV-UI-1 被定义为一条Status: locked已锁定的产品不变量StatementPurple is off-brand. UI accents and primary actions use white/neutral treatments, not purple hues, glows, or gradients.紫色是偏离品牌off-brand的。UI 强调色与主操作应使用白色/中性处理而非紫色调、辉光或渐变。locked 状态意味着它属于仓库product/invariants/目录下的产品不变量体系——这类规则一旦锁定改动必须走专门流程详见后文 PR 规则。Friend 项目是一款AI that sees your screen, listens to your conversations and tells you what to do的 AI 助手其视觉语言刻意保持克制的中性风格紫色被明确定义为品牌禁区。仓库根目录 AGENTS.md 的 Cross-Component Guidelines 中亦同步了这条红线Never use purpleanywhere in UI (icons, accents, glows, gradients) — off-brand; use white/neutral. Enforced as a no-increase ratchet (INV-UI-1); seeproduct/invariants/brand-ui.md.即界面中的图标、强调色、辉光、渐变等任何位置都不允许紫色强制执行方式为INV-UI-1的无增长棘轮。二、MUST NOT被明确禁止的行为清单文档以MUST NOT形式列出的红线非常具体共两条禁止在 UI 中使用紫色——包括图标icons、强调色accents、辉光glows、悬停状态hover states、渐变gradients。禁止引入新的紫色系统色或品牌 token——即不得新增purplePrimary、purpleGradient之类的语义色命名。值得注意的是第二条与允许存量债务存在的棘轮机制配合使用历史遗留的紫色引用可以在迁移期内存在但新增紫色无论是新文件还是已有文件中提高计数都会导致检查失败。三、适用范围哪些界面受约束INV-UI-1 覆盖三块端侧界面Surfaces端技术栈覆盖范围桌面端SwiftUIOmiTheme及桌面视图desktop/macos/Desktop/Sources/移动 AppFlutterapp/lib/下的全部 UI 代码Web营销/管理后台web/下以 Omi 品牌名义上线的界面对应的Path globs路径匹配被写进文档同时也被检查脚本作为UI_ROOTS常量原样复用见.github/scripts/check_brand_ui.py第 20–24 行desktop/macos/Desktop/Sources/** app/lib/** web/**这些路径与检查脚本中的UI_ROOTS一一对应说明文档与代码守卫是契约—实现的强绑定关系而非松散的约定。四、Guard 测试check_brand_ui.py 的无增长棘轮机制文档明确指出守卫脚本为.github/scripts/check_brand_ui.py。这是本不变量的核心工程实现值得深入拆解。4.1 棘轮ratchet的核心语义脚本 docstring 开宗明义第 1–8 行Compares purple-hit counts in changed UI sources against the merge base. Existing debt may remain; introducing new purple (raising a files count, or adding purple in a new file) fails.即只比较本次变更文件在 PR 基线上的紫色命中数变化。存量紫色债务可以继续存在但某个文件中的紫色命中数一旦上升或新文件引入紫色检查即失败。这正是无增长棘轮的含义——它不追求一步清零而是保证紫色使用量只减不增。4.2 文件过滤is_ui_source在统计紫色之前脚本先过滤哪些文件需要被监管is_ui_source第 81–91 行同时满足四个条件才算 UI 源文件不在ALLOWLIST_FILES允许列表中路径以UI_ROOTS任一前缀开头desktop/macos/Desktop/Sources/、app/lib/、web/扩展名属于UI_SUFFIXES {.swift, .dart, .ts, .tsx, .js, .jsx, .css}路径中不含.git、node_modules、build、dist、.next、__pycache__等跳过目录。4.3 检测模式从关键字到色相判定的三层防线count_purple函数第 115–121 行采用穷举模式 色相判定的双层计数每一层都对应一个真实发生过的漏检事故。第一层显式模式匹配PURPLE_PATTERNS第 39–66 行平台 APIColor.purple、.purpleSwiftUI 简写如.foregroundStyle(.purple)、Colors.purpleFlutterFlutter 特有拼写\bdeepPurple(?:Accent)?\b——注释说明\bpurple\b因 camelCase 无词边界无法命中deepPurple曾导致漏检十六进制字面量#7C3AED、#8B5CF6等 9 个枚举紫色色值PURPLE_HEX_HUES第 37 行以及 Dart 风格的0x(?:[0-9A-F]{2})?(?:7C3AED|...)0xFF8B5CF6形态lookbehind 要求0x必须作为 token 起点防止命中foo0x8B5CF6这类无关常量语义 tokenpurplePrimary|purpleSecondary|purpleAccent|purpleLight|purpleGradient|purpleLightGradientTailwind/CSSpurple-500类名、--purple-CSS 变量、color: purple、字符串Purple邻近色族\b(?:violet|indigo|fuchsia)-\d{2,4}\b——注释还原了事故经过violet/indigo 拼写与 purple 完全不同之前的模式全部漏过导致 Web 市场marketplace曾上线 violet 促销卡片与两个 violet 分类主题而检查仍报 OK。第二层HSV 色相判定is_purple_hex第 106–112 行由于枚举色值永远追不上真实世界脚本引入了第二个更智能的判定对任何 6 位十六进制字面量HEX_LITERAL第 96 行做 HSV 转换判断其是否落在人眼感知为紫色的区间PURPLE_HUE_RANGE (235.0, 320.0)HSV 色相在蓝色到品红之间视为紫色MIN_SATURATION 0.25、MIN_VALUE 0.20低于该饱和度和明度的颜色如#1A1A1A即使色相计算值落在区间内也会被视为中性色而非紫色。注释里记录了一个经典的漏检案例#6C2BD9与枚举值#6D28D9只差一个数字App Store 开发者横幅的紫色渐变就曾携带这种色值绕过成员判定通过检查——色相判定正是为此补上的兜底防线。4.4 计数与比较棘轮如何证明不增长主流程main()第 131–166 行逻辑清晰解析参数--changed-files必填列出变更文件、--base必填合并基线 git ref、--root仓库根默认.读取变更文件列表并过滤出 UI 源文件若无任何 UI 源文件变更则直接OK对每个变更文件读取当前工作区内容统计head_count用git show base:path取出基线内容统计base_count若head_count base_count记录回归path: purple hits base → head存在回归则输出FAIL: INV-UI-1 — purple usage increased in changed UI files.并列出明细退出码 1否则输出OK: INV-UI-1 — no purple increase across N changed UI file(s).退出码 0。命令行调用形态为python3 .github/scripts/check_brand_ui.py \ --changed-files /tmp/changed.txt \ --base origin/main \ --root .其中--base缺失时会直接FAIL因为棘轮必须依赖基线内容才能判断是否增长。4.5 允许列表Allowlist债务的受控出口脚本ALLOWLIST_FILES第 30–33 行目前仅含一个路径ALLOWLIST_FILES: set[str] { # Theme token definitions still expose purple* names during migration. desktop/macos/Desktop/Sources/Theme/OmiColors.swift, }被允许的原因注释得很清楚这是迁移期间仍暴露purple*命名的旧主题 token 定义文件。同时脚本明确要求允许列表应优先收缩而非扩张——如需新增条目必须在 PR 中引用 INV-UI-1 说明理由。有趣的是仓库中该文件当前实际内容desktop/macos/Desktop/Sources/Theme/OmiColors.swift显示旧的OmiColors深色寄存器已经被删除原硬编码近黑背景backgroundPrimary#0F0F0F、textPrimary#FFFFFF在改为 light-pinned 玻璃面板InkGlass后全部失效项目选择删除而非重新着色或别名a silent invisible surface is the one failure a build cannot report视图改用Ink颜色、InkType字体以及PageGlass/GlassShell/NotchGlass表面词汇表达。文件仅保留通用的Color(hex:)便利初始化器。这从侧面印证了不变量生态的运作方式文档声明红线脚本执行棘轮而存量债务通过 allowlist 与迁移逐步清除。五、测试佐证test_check_brand_ui.py仓库为count_purple、is_purple_hex、is_ui_source提供了单元测试.github/scripts/test_check_brand_ui.py其中每个测试都对应一次真实事故或边界设计测试用例验证点test_counts_color_purple_and_hexColor.purple、0x8B5CF6、#8B5CF6、purplePrimary均计数test_counts_flutter_deep_purpleColors.deepPurple/deepPurpleAccent/.shade300命中test_counts_dart_hex_literalColor(0xFF8B5CF6)、Color(0xff7c3aed)命中test_ignores_colours_that_merely_contain_purple_hex_digits0xFF1A2B3C、0xFF00FF00不计数防止误伤test_dart_hex_literal_must_start_a_tokenfoo0x8B5CF6、SOME_CONST0x8B5CF6不计数value 0x8B5CF6;计数test_is_ui_source桌面 Sources 为 UI 源backend/main.py不是allowlist 文件不算test_counts_swiftui_dot_purple.foregroundStyle(.purple)命中test_counts_tailwind_bg_purple_500bg-purple-500 text-purple-700计 2test_counts_tailwind_ramps_that_are_purple_by_sight_not_by_namebg-violet-500 text-indigo-300计 2、from-fuchsia-600计 1test_hex_is_judged_by_hue_not_by_membership#6C2BD9、#2D1B69等未枚举色值按色相判定命中test_counts_css_swift_and_dart_indigo_literals#6366F1indigo 色值计 1这些测试既锁定了检测器的行为契约也把为什么需要这层检测的历史事故固化为回归防线属于典型的不变量 守卫测试 事故驱动补丁闭环。六、与其他守卫的联动guardrail_pulse.py 的品牌指标INV-UI-1 的守卫并非孤立存在。.github/scripts/guardrail_pulse.py导入了check_brand_ui模块第 25 行在其_brand_ui_metric第 102–119 行中直接复用UI_ROOTS、is_ui_source与count_purple遍历仓库根下全部 UI 源文件并累加紫色命中数产出名为brand_ui_purple的度量指标第 119 行。这意味着仓库存在两条互补的监控视角PR 级的棘轮check_brand_ui.py阻止单个 PR 使紫色使用量上升全局脉冲度量guardrail_pulse.py持续观测整个仓库的紫色总量量化债务消减的长期趋势。同一套判定函数被两处复用也保证了局部拦截与全局度量的判定口径完全一致。七、PR 规则何时需要在 PR 中显式点名 INV-UI-1文档的PR rule刻意区分了两种情况常规 UI PR 不需要在标题或描述中点名INV-UI-1——因为品牌棘轮已经作为 CI 守卫自动兜底品牌地板由 ratchet 强制保证。只有两类场景需要显式点名一是故意变更品牌配色政策例如调整品牌色方向二是修改允许列表ALLOWLIST_FILES。这两种操作本质上是在修改不变量本身因此必须走显式引用流程。这与仓库根 AGENTS.md 的跨组件规范一致如果你的 diff 触及某条锁定不变量的 path globs必须在 PR body 中点名每一个命中的不变量 ID基于路径而非意图并在行为变化时更新该不变量的守卫测试。可借助scripts/pr-preflight --suggest自动发现命中项。八、实践建议在 Friend 仓库中如何遵守与自查结合文档与源码给读者一份可执行的自查清单改动前先确认改动文件是否落入desktop/macos/Desktop/Sources/**、app/lib/**、web/**三个 glob 之一若命中本不变量即刻生效。编码时强调色与主操作使用白色/中性色如Ink、PageGlass等桌面端表面词汇或 Flutter/Web 的中性 token不引入任何purple*语义命名不使用#7C3AED、#8B5CF6、#A855F7、#9333EA、#6D28D9、#AF52DE、#D946EF、#A78BFA、#C4B5FD等紫色系色值也避免 Tailwind 的violet-*、indigo-*、fuchsia-*色阶。本地验证运行单元测试确认检测器行为符合预期——python3 .github/scripts/test_check_brand_ui.py位于.github/scripts/目录下需要完整走一遍棘轮时可准备变更文件清单并指定基线执行check_brand_ui.py。存量债务若某文件已存在紫色且本次改动会自然触碰它优先顺带消减严禁在同一 PR 中让该文件计数上升。允许列表除非确有迁移期理由否则不要向ALLOWLIST_FILES新增路径必须新增时在脚本注释中说明原因并在 PR 描述中显式点名 INV-UI-1。策略变更任何品牌色政策的调整都属于不变量变更须显式引用 INV-UI-1 并同步更新守卫脚本与测试。九、小结一条不变量的完整工程闭环从 brand-ui.md 我们可以看到一个产品不变量在 Friend 仓库中的完整生命周期语义层一句话锁定品牌方向——No purple; neutral accents状态 locked约束层MUST NOT 清单 Surfaces Path globs明确禁什么、管哪里执行层.github/scripts/check_brand_ui.py以无增长棘轮方式在 CI 中拦截新增紫色模式匹配 HSV 色相判定双层防漏allowlist 提供受控债务出口验证层.github/scripts/test_check_brand_ui.py把历次漏检事故固化为回归用例度量层.github/scripts/guardrail_pulse.py持续统计brand_ui_purple全局总量协作层PR 规则区分常规改动自动守卫与策略变更显式点名与根目录 AGENTS.md 的 invariants 流程衔接。对任何要在 Friend 仓库中提交 UI 改动的开发者而言理解 INV-UI-1 及其棘轮实现等于同时理解了品牌红线本身、CI 拦截机制以及如何在债务与红线之间做受控的渐进式迁移——这正是仓库把一条视觉规则工程化为可强制执行、可度量、可持续消减的系统性做法。【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表