Files
inkreach-agent/docs/plugins/DESIGN.md
T

8.5 KiB
Raw Blame History

DESIGN.md · 插件视觉一致性规范(beautifului 审美)

读者是写 UI 的 Agent/开发者。目标:任何来源的插件,界面观感与宿主完全一致, 达到 beautifului.dev(MIT)的质感。 规则分两类:强制(可机器校验/一眼可辨)与默认(无充分理由不得偏离)。 契约问题看 ../plugins.md;本文件只管"长什么样"。 Token 单一事实源:apps/desktop/src/renderer/src/styles/beautifului.css(= foundation 原版), 组件参考源码归档在 docs/ui-ref/beautifului/*.json(26 个 registry)。

0. 两条铁律

  1. 色值只允许来自 token。任何 #hex/rgb()/oklch() 硬编码(规范中列出的除外)都算违规; 语义永远用 token 名表达(text-ink-3 而不是 text-gray-400)。
  2. 不发明新组件形态。先查本文 §4 标准模式与公共组件库;确需新形态时, 用 §1 的 token 组合拼装,不引入新色/新圆角/新阴影。

1. Token 表(单一事实源:peer://assets/tokens.css = 宿主 beautifului.css)

表面(背景)——冷调近白,亮暗两套完整 token

token 意图
--page 页面最底层背景
--canvas 画布(primary 按钮的文字色取这里)
--surface 卡片/面板表面(纯白)
--inset 内嵌区域(输入框底、预览底、代码块底)
--hover / --hover-2 悬停态一级/二级

文字——中性 ink ramp(深灰,不用纯黑)

token 意图
--ink 主文字(标题、关键值)
--ink-2 次级文字(正文说明)
--ink-3 弱化文字(占位、提示、元信息、路径)

边线——实色 hairline,不用 alpha

token 意图
--line 常规边框(卡片、列表分隔)
--line-strong 强边框(输入框、可点击描边)
--line-soft 极弱分隔

强调与语义——蓝色 accent,语义色只作"调味料"

token 意图
--accent / --accent-ink / --accent-tint 焦点环/选区/选中态/链接(accent-ink 负责可交互文本,保 AA 对比)
--green / --green-tint 成功/正常
--orange / --orange-tint 警示
--red / --red-tint 错误/危险

阴影——「ring + 分层柔影」,不是纯 hairline

token 意图
--shadow-btn ring(line-strong) + xs——secondary 按钮
--shadow-card ring(line) + sm——卡片
--shadow-raised ring(line) + md——悬浮件
--shadow-overlay ring(line) + lg——弹层/模态
--shadow-inset-field 输入框内嵌阴影
--shadow-hairline 纯 ring(无投影)

暗色模式 ring 自动切换为低透明白渐进(0.10→0.15 按浮起高度),阴影加深—— 黑白主题不是简单反色:暗色有一套专属 elevation 表达。

圆角体系(关键差异)

token 值 用途
--radius-chip 6px chips、小徽标
--radius-control 8px 输入框、菜单行、icon 按钮
--radius-card 10px 卡片
--radius-window 14px 窗口级容器(composer 外壳、弹窗)
rounded-full 999px 按钮(pill 是核心语言)、状态点、头像

动效——强缓动曲线,内置 ease 太弱

token 值 用途
--ease-out-strong cubic-bezier(0.23, 1, 0.32, 1) 大位移(滑层、展开、fade-up)
--ease-in-out-strong cubic-bezier(0.77, 0, 0.175, 1) 往返位移
--ease-link cubic-bezier(0.16, 1, 0.3, 1) 下划线/微交互
控件默认 150ms ease-out pill 按钮、悬停反馈
按压 active:scale-[0.96] 所有可点 pill

2. 类名词汇(宿主 Tailwind 映射,renderer 组件直接用)

背景 bg-page / bg-canvas / bg-surface / bg-inset / bg-hover / bg-hover-2 / bg-accent / bg-accent/10 · 文字 text-ink / text-ink-2 / text-ink-3 / text-accent / text-accent-ink / text-red · 边框 border-line / border-line-strong / border-dashed border-line · 圆角 rounded-chip(6)rounded-control(8)rounded-card(10)rounded-window(14)rounded-full(pill)· 阴影 shadow-btn / shadow-card / shadow-raised / shadow-overlay。

3. 字号与间距

  • 基准 14px/1.5,letter-spacing -0.01em;字号阶:11px(元信息/徽标)→ 12px(chips、正文说明)→ 12.5/13px(行标题、按钮)→ 14px(正文)→ 16px(页面标题)。不使用其他字号。
  • 间距基数 4px:卡片内边距 px-3 py-2.5(紧凑)或 primitive-card-pad(12px),区块间 space-y-1.5,小节间 pt-3。
  • 图标 12–16px 线条风格,currentColor;一律 lucide(宿主白名单 plugin-icons.tsx),禁止自绘 svg。
  • 颜色只作"调味料":结构永远是中性 ink + line,彩色仅出现在状态/数值/选中。

4. 标准模式

renderer 组件优先用组件库:@aso/plugin-sdk/ui 的 Button/SettingRow/LoadingState/ ErrorState/EmptyState/Badge… 已按本规范实现(见 L6)。 下面的 HTML 形态留给 iframe 插件页作者。

按钮(pill 是核心语言)

<!-- primary:墨底 canvas 字 + 内高光;accent:蓝底白字;secondary:白底 ring+xs -->
<button class="inline-flex h-[27px] items-center justify-center gap-1.5 rounded-full px-3
  text-[13px] font-medium leading-none transition-[transform,background-color,opacity]
  duration-150 ease-out active:scale-[0.96] bg-accent text-white hover:bg-accent-ink
  shadow-[inset_0_1px_0_rgba(255,255,255,0.14)]">主操作</button>
<!-- quiet:透明直到悬停(密集工具行) -->
<button class="h-7 rounded-full px-2.5 text-[12px] text-ink hover:bg-hover">次要</button>

设置行(label 左 / 控件右)

<div class="flex items-center justify-between gap-3 rounded-card border border-line px-3 py-2.5">
  <div class="min-w-0">
    <div class="text-[12.5px] text-ink">标题</div>
    <div class="mt-0.5 text-[11px] leading-4 text-ink-3">说明文字</div>
  </div>
  <!-- 控件 -->
</div>

卡片(window 级容器用 14px)

<div class="rounded-window border border-line bg-surface shadow-card">…</div>

徽标 / 空态 / 开关

徽标 rounded-chip bg-hover px-1.5 py-0.5 text-[10px] text-ink-3; 空态 = 虚线卡(同旧版);开关 h-5 w-9(行内)/ h-6 w-10(表单级), 滑块 size-4/5 rounded-full bg-white shadow,开 bg-accent、关 bg-line-strong。

5. 动效范式

  • 悬停反馈:150ms ease-out,只变 background-color/text(配 transition-colors)
  • 内容进场:fade-up 400ms var(--ease-out-strong)(聊天消息、卡片)
  • 弹层:pop-in(scale 0.95→1)+ shadow-overlay
  • 滑动高亮层(菜单/标签条):220ms cubic-bezier(0.23,1,0.32,1)(GlideMenu)
  • 流式输出:光标 2px --ink 常亮→结束后闪烁;prefers-reduced-motion 全局降级

6. iframe 插件页附加规则

  • <head> 必须引入 <link rel="stylesheet" href="peer://assets/tokens.css" />,并用同名 token 编译自己的 Tailwind
  • 主题跟随宿主推送(useHarnessTheme),禁止 prefers-color-scheme 自测;亮暗两套 token 都要核对
  • 与宿主的视觉差异应趋近于零:同 token + 同类名词汇 + 同字号阶 + 同圆角/阴影体系

7. renderer 组件附加规则

  • 类名直接用宿主词汇(§2),无需引样式;优先 import 公共组件库(L6),不要手写标准模式
  • React 必须来自 ctx.react(见 L4),否则 hooks 失效
  • 禁止操作宿主 DOM(document 全局查询/改样式),一切交互限制在自己的组件子树
  • 机器关卡:pnpm plugin:check <插件目录>(hex 色值 / React 副本 / 三态缺失等,见 L7)

8. 自检清单(提发布前)

  • pnpm plugin:check <插件目录> 0 error(机器关卡,见 L7)
  • 无硬编码色值/阴影/圆角(铁律 1)
  • 圆角落在体系上:chip 6 / control 8 / card 10 / window 14 / pill 999
  • 字号都在 §3 的阶上;按钮是 pill(rounded-full)
  • 深浅色都检查过(暗色不是反色:ring 是低透白、阴影更深)
  • 复用了 §4 模式(renderer 直接用组件库)而非新造形态
  • iframe 页引了 tokens.css 且无 prefers-color-scheme