diff --git a/AGENTS.md b/AGENTS.md index 0248d64a1..2d9cd1402 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,6 +50,7 @@ Two React apps that **must not be mixed**. Per-app rules auto-load from each sub - **Never** introduce new UI or state-management libraries. - All code comments in English. - 403 handled automatically by response interceptors — never add 403 branches in business code. +- **No frosted glass by default** — no `backdrop-filter` / `backdrop-blur-*`, including arbitrary values (`backdrop-blur-[4px]`), variant prefixes (`hover:backdrop-blur-sm`) and the arbitrary-property form `[backdrop-filter:blur(…)]`. Every such element gets its own compositing layer and re-snapshots + re-blurs its backdrop each frame; without GPU acceleration — the norm on 信创 machines — that runs on the CPU. A customer on 信保安全浏览器 (Chromium 108) had scrolling *and mouse movement* stall in daily chat; forcing `backdrop-filter: none` fixed it outright. Cost is **per element, not per radius**: a 4px blur on a 24px button costs the same order as a full-screen one, and the per-message action buttons multiplied it by conversation length. Use a translucent background instead (`bg-white/80`, `bg-black/40`) — past ~70% opacity the blur was invisible anyway. **Only exception**, and it must be justified: a full-screen overlay of which at most one exists at a time, verified on a 信创 browser. Never on anything that scales with content (list rows, message bubbles, cards, notification items). Rationale + alternatives: `packages/ui/docs/基础-阴影与圆角规范.mdx` §3. - **i18n**: no hardcoded Chinese in source (lint-enforced; legacy frozen). New keys ship all three languages (zh-Hans/en/ja) in the same PR. Error-code copy lives ONLY in `src/frontend/packages/locales` (`api_errors` domain — platform addresses `api_errors:`, client `api_errors.`); its generated artifacts (`platform/public/locales/*/api_errors.json`, `client/src/locales/*/api_errors.gen.json`) are never edited by hand (CI-checked). CI also runs `pnpm check-i18n` — key parity across languages + backend error-code coverage; legacy drift is frozen in `scripts/i18n-baseline.json` (shrink-only, `--update-baseline` after healing). Legacy hardcoded Chinese is paid down by whoever touches the file: when editing a file with frozen violations, extract its Chinese strings to i18n (`/i18n-localizer`) in the same change. See `packages/locales/README.md`. - **Quality gate (CI-enforced, `frontend-quality.yml`)**: `pnpm lint` + `pnpm typecheck` (run from `src/frontend/`) must pass. Legacy violations are frozen — ESLint in each app's `eslint-suppressions.json`, TS strict via `// @ts-strict-ignore` file headers — and may only shrink: never hand-edit the suppressions file, never add `@ts-strict-ignore` to a new file. After fixing violations in a file, run `pnpm lint:prune` (per app) and delete its `@ts-strict-ignore` header if it now passes strict. diff --git a/src/frontend/client/src/Providers/ConfirmContext.tsx b/src/frontend/client/src/Providers/ConfirmContext.tsx index 7556ed85a..0a07b4e45 100644 --- a/src/frontend/client/src/Providers/ConfirmContext.tsx +++ b/src/frontend/client/src/Providers/ConfirmContext.tsx @@ -131,7 +131,7 @@ export const ConfirmProvider = ({ children }: { children: React.ReactNode }) => {!options.hideCancel && {options.cancelText || defaultCancel} } diff --git a/src/frontend/client/src/components/Chat/AiChatMessages.tsx b/src/frontend/client/src/components/Chat/AiChatMessages.tsx index 49daa98f0..bca364081 100644 --- a/src/frontend/client/src/components/Chat/AiChatMessages.tsx +++ b/src/frontend/client/src/components/Chat/AiChatMessages.tsx @@ -462,7 +462,7 @@ export default function AiChatMessages({ @@ -1122,8 +1122,8 @@ export function NotificationsDialog({ onClick={() => setOnlyUnread((v) => !v)} className={ onlyUnread - ? "h-8 shrink-0 rounded-md border border-transparent bg-[rgb(var(--brand-500)/0.2)] px-4 py-[5px] text-[14px] font-normal text-blue-500 [backdrop-filter:blur(8px)]" - : "h-8 shrink-0 rounded-md border border-[#EBECF0] bg-white/50 px-4 py-[5px] text-[14px] font-normal text-[#212121] [backdrop-filter:blur(8px)]" + ? "h-8 shrink-0 rounded-md border border-transparent bg-[rgb(var(--brand-500)/0.2)] px-4 py-[5px] text-[14px] font-normal text-blue-500" + : "h-8 shrink-0 rounded-md border border-[#EBECF0] bg-white/50 px-4 py-[5px] text-[14px] font-normal text-[#212121]" } > {localize("com_notifications_unread_only")} @@ -1135,7 +1135,7 @@ export function NotificationsDialog({ handleMarkAllAsRead(); }} variant="outline" - className="h-8 shrink-0 rounded-md border-transparent bg-[#F8F8F8] px-4 py-[5px] text-[14px] font-normal text-[#212121] [backdrop-filter:blur(8px)]" + className="h-8 shrink-0 rounded-md border-transparent bg-[#F8F8F8] px-4 py-[5px] text-[14px] font-normal text-[#212121]" > {localize("com_notifications_mark_all_read")} diff --git a/src/frontend/client/src/components/ui/AlertDialog.tsx b/src/frontend/client/src/components/ui/AlertDialog.tsx index a17d60126..246b3080e 100644 --- a/src/frontend/client/src/components/ui/AlertDialog.tsx +++ b/src/frontend/client/src/components/ui/AlertDialog.tsx @@ -24,7 +24,7 @@ const AlertDialogOverlay = React.forwardRef< >(({ className = '', ...props }, ref) => ( {buttons != null ? buttons : null} {showCancelButton && ( - + {Cancel} )} diff --git a/src/frontend/client/src/components/ui/OriginalDialog.tsx b/src/frontend/client/src/components/ui/OriginalDialog.tsx index 6e3a05ad6..c3f9dcc4d 100644 --- a/src/frontend/client/src/components/ui/OriginalDialog.tsx +++ b/src/frontend/client/src/components/ui/OriginalDialog.tsx @@ -39,7 +39,7 @@ const DialogOverlay = React.forwardRef< diff --git a/src/frontend/client/src/pages/knowledge/FilePreview/MediaPlayer.tsx b/src/frontend/client/src/pages/knowledge/FilePreview/MediaPlayer.tsx index 4f9985da3..3329cc710 100644 --- a/src/frontend/client/src/pages/knowledge/FilePreview/MediaPlayer.tsx +++ b/src/frontend/client/src/pages/knowledge/FilePreview/MediaPlayer.tsx @@ -381,8 +381,8 @@ export function MediaPlayer({ kind, src, allowDownload = false, onDownload }: Me /* Menus float over the media, so they stay translucent and open upward rather than covering the stage below the bar. */ const menuSurfaceClass = onDarkStage - ? "border-0 bg-black/75 backdrop-blur-md" - : "bg-white/85 backdrop-blur-md"; + ? "border-0 bg-black/75" + : "bg-white/85"; /* The shared menu item forces a dark label while highlighted, which is unreadable on the dark surface — keep it white on hover/focus too. */ const menuItemClass = onDarkStage diff --git a/src/frontend/client/src/pages/knowledge/SpaceDetail/EditTagsModal.tsx b/src/frontend/client/src/pages/knowledge/SpaceDetail/EditTagsModal.tsx index cec590a3a..5092de9de 100644 --- a/src/frontend/client/src/pages/knowledge/SpaceDetail/EditTagsModal.tsx +++ b/src/frontend/client/src/pages/knowledge/SpaceDetail/EditTagsModal.tsx @@ -322,7 +322,7 @@ export function EditTagsModal({ diff --git a/src/frontend/client/src/pages/knowledge/index.tsx b/src/frontend/client/src/pages/knowledge/index.tsx index 6b91f80d7..241dd6861 100644 --- a/src/frontend/client/src/pages/knowledge/index.tsx +++ b/src/frontend/client/src/pages/knowledge/index.tsx @@ -710,7 +710,7 @@ export default function Knowledge() { {/* Drag and Drop Overlay */} {isDragging && (
{dragError ? ( diff --git a/src/frontend/client/src/routes/RouteErrorBoundary.tsx b/src/frontend/client/src/routes/RouteErrorBoundary.tsx index 6221ab25b..f21dcae2b 100644 --- a/src/frontend/client/src/routes/RouteErrorBoundary.tsx +++ b/src/frontend/client/src/routes/RouteErrorBoundary.tsx @@ -127,7 +127,7 @@ export default function RouteErrorBoundary() { role="alert" className="flex min-h-screen flex-col items-center justify-center bg-surface-primary bg-gradient-to-br" > -
+

Oops! Something Unexpected Occurred

diff --git a/src/frontend/packages/ui/docs/00-总纲.md b/src/frontend/packages/ui/docs/00-总纲.md index 562ff6155..afb8254c7 100644 --- a/src/frontend/packages/ui/docs/00-总纲.md +++ b/src/frontend/packages/ui/docs/00-总纲.md @@ -148,6 +148,7 @@ npm run dev # 开发服务器,端口 4001 - UI 组件在 `~/components/ui/`;**不引入新 UI 库**。 - 品牌色蓝⇄绿主题:**不硬编码品牌 hex**,用 `blue-*` 类(已重指向 `--brand-*`)或 `rgb(var(--brand-NNN))`。 - 语义色(成功/危险/警告)不参与换肤。 +- **默认不用毛玻璃 `backdrop-filter`**:信创环境无硬件加速,每个毛玻璃元素每帧都要重做背景快照与模糊,已导致客户端滚动卡死。改用半透明纯色;例外仅限同时最多一个的全屏遮罩,见 [基础-阴影与圆角规范.mdx](基础-阴影与圆角规范.mdx) §3。 - 详见 `src/frontend/client/CLAUDE.md`。 ## 七、图标规则(设计师偏好,优先级高于项目历史习惯) diff --git a/src/frontend/packages/ui/docs/01-设计规范.md b/src/frontend/packages/ui/docs/01-设计规范.md index 1e948fbab..1c92064e2 100644 --- a/src/frontend/packages/ui/docs/01-设计规范.md +++ b/src/frontend/packages/ui/docs/01-设计规范.md @@ -13,7 +13,7 @@ | 色彩(品牌/中性/语义/标签/插画) | [基础-色彩规范.md](基础-色彩规范.md) | ✅ v1 定稿,token 已落地 | | 字体(字体栈/九档字号/字重) | [基础-字体规范.md](基础-字体规范.md) | ✅ v1,token 已落地 | | 多端适配(双判定口径 + 4 原则) | [基础-多端适配原则.md](基础-多端适配原则.md) | ✅ v1 | -| 阴影与圆角(圆角八档/投影两档) | [基础-阴影与圆角规范.mdx](基础-阴影与圆角规范.mdx) | 🟨 v1 提案(2026-07-23),待设计师验收 → 落 token | +| 阴影与圆角与模糊(圆角八档/投影两档/毛玻璃默认禁用) | [基础-阴影与圆角规范.mdx](基础-阴影与圆角规范.mdx) | 🟨 v1 提案(2026-07-23),待设计师验收 → 落 token | | 按钮(双轴类型/尺寸/状态) | [组件-Button按钮.md](组件-Button按钮.md) | ✅ v1 定稿,基准组件已重构 | | 弹窗(遮罩/壳解剖/收敛方向) | [组件-Modal弹窗.md](组件-Modal弹窗.md) | 🟨 进行中,待定夺 | | 间距/层级(本文) | 01-设计规范.md | 🟨 待定 | diff --git a/src/frontend/packages/ui/docs/基础-阴影与圆角规范.mdx b/src/frontend/packages/ui/docs/基础-阴影与圆角规范.mdx index 52c49b455..810006cfa 100644 --- a/src/frontend/packages/ui/docs/基础-阴影与圆角规范.mdx +++ b/src/frontend/packages/ui/docs/基础-阴影与圆角规范.mdx @@ -220,7 +220,44 @@ BISHENG 的投影走「淡」路线:带一点蓝黑色、透明度很低,负 | 下拉面板:`shadow-popup`(浮层投影) | `shadow-lg`(Tailwind 纯黑预设) | 一次违反两条规则:投影不在两档内,颜色是纯黑——又重又灰,和蓝黑淡投影混排会打乱层次 | | 静态卡片:只用边框分层 | 页面里每张卡都加投影 | 投影表达的是「浮起的高度」,静态内容浮起来没有意义,还让真正的浮层失去对比 | -## 3. 使用约定 +## 3. 模糊与毛玻璃 Blur + +**默认不用毛玻璃。** 想「透出底层」用半透明纯色,想要材质感用半透明加一层很淡的噪点。 + +### 3.1 为什么 + +毛玻璃(`backdrop-filter`)要求浏览器每一帧都把元素背后的画面截下来、模糊、再合成,而且每个这样的元素都单独占一个合成层。没有硬件加速的机器只能拿 CPU 算 —— 信创环境普遍如此。 + +2026-08-04,客户在信保安全浏览器(内核 Chromium 108)上打开日常会话,滚动乃至鼠标移动都卡住;在控制台强制 `backdrop-filter: none` 后立刻恢复。 + +代价按**元素个数**算,不按模糊半径算:24px 小按钮上的 4px 模糊,和一整块全屏遮罩是同一量级。当时消息气泡的操作按钮每条回答一组,几十轮会话就是几十个合成层,再叠上 hover 变色 —— 鼠标划过就重绘,重绘就重做模糊。 + +### 3.2 用什么替代 + +| 想要的效果 | 用这个 | 说明 | +|---|---|---| +| 透出底层内容 | 半透明纯色 `bg-white/80`、`bg-black/40` | 不透明度到 70% 以上,和真毛玻璃肉眼无差 | +| 「玻璃材质」的质感 | 半透明 + 极淡噪点纹理 + 1px 内高光 | 不产生合成层 | +| 背景固定的大块区域 | 预先模糊好的图片资源 | 仅限背景不随内容变化时 | + +不要用「复制一份背景再 `filter: blur()`」的老办法 —— 同样要模糊、同样要合成层,还多一份 DOM 要同步,比毛玻璃更贵。 + +### 3.3 唯一可申请的例外 + +同时最多只存在一个的**全屏遮罩**可以申请保留,但要在信创环境实测通过。 + +**数量随内容增长的元素一律禁止** —— 列表项、消息气泡、卡片、通知条里的按钮都不行。上面那次卡死就出在这里。 + +### 3.4 正误对照 + +✅ 弹窗遮罩 `bg-black/40`,不加模糊 +✅ 吸顶栏 `bg-white/80`,不加模糊 +❌ 消息操作按钮 `backdrop-blur-[4px]` —— 数量随会话增长 +❌ 背景已经 90% 不透明还加 `backdrop-blur-md` —— 看不见,纯烧 CPU + +--- + +## 4. 使用约定 - **圆角与投影一律取表内档位**:出现表外的手写值即视为未走规范;确属例外的,在对应组件文档里写明原因并注明「见阴影与圆角规范」。 - **新档位先进规范、再进代码**:要用新的圆角或投影值,先由设计师在本文与 design token 里定档,之后才能出现在代码里,不允许先用后补——这就是本站所说「规范评审」的全部含义:设计师定夺 + 文档与 token 先行。 @@ -276,6 +313,7 @@ BISHENG 的投影走「淡」路线:带一点蓝黑色、透明度很低,负 5. `design-token.mdx` 增加「阴影 Shadow」节,从 SSOT 渲染(SHADOW 进 cjs 之后做,别手抄值)。 6. 待决策(不阻塞 v1):① 两档投影色是否统一为同一蓝黑基色(现浮层 rgba(0,23,66,·) vs 模态 rgba(3,7,117,·),肉眼几乎无差);② `.shadow-stroke`(13 处)/ `shadow-outline`(6 处)两个自定义类的去留;③ 暗色模式是否为投影单独给值(现方案:同值不调);④ 下拉基准(DropdownMenu / Select)是否补 1px 边框(边框已改为非硬性,见 §2.2)。 7. 组件-Modal弹窗.md 待决策清单「圆角:弹窗用几 px?」可勾销,标注「2026-07-23 定 16px,见基础-阴影与圆角规范」。 +8a. §3 毛玻璃的 agent 执行口径:禁用 `backdrop-blur-*`(含 `backdrop-blur-[4px]` 等任意值、`hover:` 等变体前缀)与任意属性写法 `[backdrop-filter:blur(…)]`;CSS 里同样禁 `backdrop-filter` / `-webkit-backdrop-filter`。2026-08-04 已全量清除 client 33 处 + platform 7 处,含两处只在 `@apply` 里出现、grep `backdrop-blur` 抓不到的写法。 8. 归并迁移照总纲节奏:设计师逐批点名、每批一笔提交;映射表见附录 A.1,投影迁移优先加 shadow-lg 41 处(下拉/浮层聚集)。 (sidebar 注册与 01-设计规范 指针已随本次建档完成:rspress.config.ts「设计规范」组新增本页入口,改 config 后需重启 dev 服务。) */} @@ -285,5 +323,6 @@ BISHENG 的投影走「淡」路线:带一点蓝黑色、透明度很低,负 | 日期 | 改了什之 | 涉及 | 提交 | |---|---|---|---| | 2026-07-23 | 建档 v1 提案:圆角八档使用规则(控件跟尺寸 4/6/8、容器 12/16/24/32、弹窗定 16px 终结 8vs16、嵌套=外层−内边距、禁任意值);投影两档 popup/modal(取下拉/弹窗基准组件已落地值)+「淡投影+1px 边框」配套规则;全量现状扫描存档(rounded 1429 处 / shadow 260 处,口径见附录 A);01-设计规范 圆角/阴影两节迁出归口本文,总纲看板与 rspress 侧栏同步 | 本文件、01-设计规范.md、00-总纲.md、rspress.config.ts | 待 committer 窗口提交 | +| 2026-08-04 | 新增 §3 模糊与毛玻璃:默认禁用 `backdrop-filter`,给出半透明纯色/噪点/预模糊图三条替代,例外仅限同时最多一个的全屏遮罩且需信创实测;原 §3 使用约定顺延为 §4。起因为客户在信保安全浏览器(Chromium 108,无硬件加速)上日常会话滚动与鼠标移动卡死,强制关闭毛玻璃后恢复 | 本文件、根 AGENTS.md、00-总纲.md、01-设计规范.md | 待 committer 窗口提交 | | 2026-07-23 | v1.2 展示层去迁移工程措辞:正文(§1.2/1.3/1.4/1.6/2.1/2.2/3)删「已落地 / 现行基准 / 验收后 / 接线完成前 / 迁移归并 / 存量归并」等迁移状态描述,只留设计规则;迁移事实与归并映射原样保留在隐藏区(附录 A、给实现窗口、本改动记录) | 本文件 | 待 committer 窗口提交 | | 2026-07-23 | v1.1 按设计师 7 条批注修订:① 工作档头补「控件/容器两分法」来源口径;② 新增 §1.4 贴边直角(移动端全屏/贴底容器贴边侧直角,认可审批中心先例,多端适配原则加指针);③ §2.1 说明 token 与 Tailwind 类名同名同物、验收后进 design token 页;④「投影必须搭 1px 边框」降级为非硬性搭配(§2.2 末条),相关 ❌ 例与附录锚点同步改;⑤「只用两档」正文改白话,精确类名清单移隐藏区(给实现窗口 1a);⑥「不用纯黑投影」升级为 §2.2 规则条;⑦ §3「规范评审」写实为「设计师定夺 + 先改规范再写代码」 | 本文件、基础-多端适配原则.md | 待 committer 窗口提交 | */} diff --git a/src/frontend/platform/src/components/LinSight/skill/SkillFormDrawer.tsx b/src/frontend/platform/src/components/LinSight/skill/SkillFormDrawer.tsx index 7538b74b6..a4b7f0979 100644 --- a/src/frontend/platform/src/components/LinSight/skill/SkillFormDrawer.tsx +++ b/src/frontend/platform/src/components/LinSight/skill/SkillFormDrawer.tsx @@ -212,7 +212,7 @@ export function SkillFormDrawer({ open, editing, onOpenChange, onSaved }: SkillF
{/* footer — actions only */} -
+
diff --git a/src/frontend/platform/src/pages/BuildPage/flow/FlowChat/DragDropOverlay.tsx b/src/frontend/platform/src/pages/BuildPage/flow/FlowChat/DragDropOverlay.tsx index 23d4e6e3f..339ecd03d 100644 --- a/src/frontend/platform/src/pages/BuildPage/flow/FlowChat/DragDropOverlay.tsx +++ b/src/frontend/platform/src/pages/BuildPage/flow/FlowChat/DragDropOverlay.tsx @@ -58,10 +58,9 @@ export default function DragDropOverlay() { return (

{t('com_addAnything')}

diff --git a/src/frontend/platform/src/pages/Dashboard/components/charts/ChartContainer.tsx b/src/frontend/platform/src/pages/Dashboard/components/charts/ChartContainer.tsx index 347a2dec0..d962773f7 100644 --- a/src/frontend/platform/src/pages/Dashboard/components/charts/ChartContainer.tsx +++ b/src/frontend/platform/src/pages/Dashboard/components/charts/ChartContainer.tsx @@ -59,7 +59,7 @@ export function ChartContainer({ isPreviewMode, isDark, component }: ChartContai }} /> -
+
{t('updatingCharts')} diff --git a/src/frontend/platform/src/pages/Dashboard/components/editor/ComponentWrapper.tsx b/src/frontend/platform/src/pages/Dashboard/components/editor/ComponentWrapper.tsx index 95a372fbe..b8a68a45d 100644 --- a/src/frontend/platform/src/pages/Dashboard/components/editor/ComponentWrapper.tsx +++ b/src/frontend/platform/src/pages/Dashboard/components/editor/ComponentWrapper.tsx @@ -154,7 +154,7 @@ export const ComponentWrapper = memo(({ diff --git a/src/frontend/platform/src/routes/RouteErrorBoundary.tsx b/src/frontend/platform/src/routes/RouteErrorBoundary.tsx index feb7a9d9b..f0d2b74f6 100644 --- a/src/frontend/platform/src/routes/RouteErrorBoundary.tsx +++ b/src/frontend/platform/src/routes/RouteErrorBoundary.tsx @@ -127,7 +127,7 @@ export default function RouteErrorBoundary() { role="alert" className="flex min-h-screen flex-col items-center justify-center bg-surface-primary bg-gradient-to-br" > -
+
diff --git a/src/frontend/platform/src/style/applies.css b/src/frontend/platform/src/style/applies.css index b84cdc170..41de151d4 100644 --- a/src/frontend/platform/src/style/applies.css +++ b/src/frontend/platform/src/style/applies.css @@ -620,7 +620,7 @@ } .send-message-modal-transition { - @apply fixed inset-0 bg-black bg-opacity-80 backdrop-blur-sm transition-opacity + @apply fixed inset-0 bg-black bg-opacity-80 transition-opacity } .chat-modal-box { @apply fixed inset-0 z-10 overflow-y-auto