Skip to content

refactor(web): 液态玻璃视觉重构 + 全站中文化 - #9

Merged
wha7ev9r merged 3 commits into
mainfrom
refactor/frontend-redesign
Jul 31, 2026
Merged

refactor(web): 液态玻璃视觉重构 + 全站中文化#9
wha7ev9r merged 3 commits into
mainfrom
refactor/frontend-redesign

Conversation

@wha7ev9r

@wha7ev9r wha7ev9r commented Jul 31, 2026

Copy link
Copy Markdown
Owner

变更概览

对前端做完整的 UI/UX 重构:暗色终端极客风 → 液态玻璃(liquid glass)风格,并全站中文化。

设计系统(web/src/style.css)

  • 新增 @utility 玻璃表面:glass(半透明渐变 + 高光描边 + backdrop-blur)、glass-hover(hover 紫/青辉光)、glass-inset(内嵌命令卡)
  • 背景增加顶部紫/青径向辉光;kicker 改为紫→青渐变文字
  • 按钮/输入/表格/标签统一圆角(6px/10px)、hover 辉光、active 按压反馈

新通用组件(web/src/components/)

Panel(卡片容器)、CodeBlock(代码块+复制)、CopyButton、StatusDot(发光状态点)、Banner(错误/成功提示)、EmptyState、Logo(立方体 logo 抽取复用)

页面重构

  • App.vue:玻璃 header、激活导航紫色渐变指示条、logo hover 辉光、页面过渡微调
  • Dashboard:玻璃统计卡(手绘图标)、traffic 面板增强(总趋势大图:渐变面积+网格线+峰值圆点、总/峰值/均值/活跃镜像指标卡)、sparkline 紫青渐变+面积填充、健康概览玻璃卡
  • Mirrors/Search/Releases/Settings/GitProxy/Login:统一玻璃面板、表格、按钮;Releases 添加源表单补充字段详细说明(校验规则、精确匹配等)
  • 全站 UI 文案中文化(按钮/表头/面板标题/状态标签/提示语),命令代码内容保持原样

Bug 修复

  • 移动端横向溢出:所有响应式 grid 补 grid-cols-1(单列 auto 轨道被代码块 nowrap / 表格 min-w-max 撑宽的根因),375px 下已无横向滚动
  • 移动端代码块自动换行显示(桌面端保持单行滚动)

其他

  • 新增根目录 AGENTS.md(项目开发约定)
  • 新增 docs/frontend-refactor-prompt.md(本次重构的任务提示词存档)

验证

  • pnpm run build 通过(vue-tsc 无类型错误)
  • 后端 API 契约未变(has_more / per_page 等保持后端 JSON tag)
  • 无新增 npm 依赖
  • 375px 移动端截图验证无横向溢出
  • 逐页 Edge headless 截图 + 视觉模型检查

Summary by CodeRabbit

  • 新功能
    • 新增统一的品牌标识、状态指示、提示横幅、面板、代码展示、复制按钮及空状态组件。
    • 代码块支持一键复制,并提供复制成功反馈。
  • 界面优化
    • 全面升级登录、搜索、镜像、发行版、设置及 Git 代理页面的视觉设计。
    • 导航、表单、表格、按钮和页面过渡效果更加统一,并适配响应式布局。
  • 本地化
    • 主要导航、页面文案、状态信息和操作提示改为中文。
  • 文档
    • 新增项目开发指南及前端 UI/UX 重构规范文档。

- Introduce glass design system (@Utility glass/glass-hover/glass-inset)
- New shared components: Panel, CodeBlock, CopyButton, StatusDot, Banner, EmptyState, Logo
- Redesign App shell: sticky glass header, purple gradient active nav indicator, logo glow
- Redesign Dashboard: glass stat cards, enhanced traffic panel (total trend chart with gradient area + stat chips), gradient sparklines
- Localize all UI copy to Chinese across 7 pages; add detailed field hints on Releases add-source form
- Fix mobile overflow: add grid-cols-1 to responsive grids; code blocks wrap on small screens
- Add AGENTS.md project conventions
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The head commit changed during the review from 32b0771 to fb5fc55.

📝 Walkthrough

Walkthrough

新增共享 UI 组件,统一全局视觉样式,并将登录、代理、镜像、发布、搜索和设置页面本地化为中文。新增开发指南和前端重构任务说明。

Changes

前端界面重构

Layer / File(s) Summary
项目与重构规范
AGENTS.md, docs/frontend-refactor-prompt.md
新增开发环境、组件约定、视觉规范、验收标准和工作流说明。
共享 UI 组件
web/src/components/*
新增 BannerCodeBlockCopyButtonEmptyStateLogoPanelStatusDot
主题样式与应用外壳
web/src/style.css, web/src/App.vue, web/src/components/StatusCard.vue
新增紫色和青色主题、玻璃样式工具类、状态样式、导航状态和页面转场效果。
登录与代理页面集成
web/src/views/Login.vue, web/src/views/GitProxy.vue
接入共享组件,更新加载与错误状态,移除本地复制逻辑,并统一中文文案。
数据页面组件集成
web/src/views/Mirrors.vue, web/src/views/Releases.vue, web/src/views/Search.vue, web/src/views/Settings.vue
接入共享面板、提示、状态点和空状态组件,更新中文标签、表单、分页及状态展示。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了前端液态玻璃视觉重构和全站中文化这两项主要变更。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/frontend-redesign

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/views/Dashboard.vue (1)

177-181: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

同步粒度选择和异步响应状态。

当请求失败时,选择器会显示新粒度,但图表仍使用旧数据。快速切换时,较早响应也可能覆盖较新响应。为请求分配递增 ID 或取消旧请求。仅在最新请求成功后同时提交粒度和数据。失败时保留旧状态并显示错误。

建议的最小修复
+let granularityRequestId = 0
+
 async function switchGranularity(level: 'hourly' | 'daily' | 'weekly') {
-  chartGranularity.value = level
-  const data = await getTraffic(undefined, undefined, level)
-  hourlyTraffic.value = Array.isArray(data) ? data : []
+  const requestId = ++granularityRequestId
+  try {
+    const data = await getTraffic(undefined, undefined, level)
+    if (requestId !== granularityRequestId) return
+    hourlyTraffic.value = Array.isArray(data) ? data : []
+    chartGranularity.value = level
+  } catch {
+    if (requestId === granularityRequestId) {
+      errorMsg.value = '流量数据加载失败'
+    }
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/views/Dashboard.vue` around lines 177 - 181, 更新
switchGranularity,为每次请求分配递增请求 ID 或取消前一个请求,并仅允许最新请求成功后同时更新 chartGranularity 和
hourlyTraffic;请求失败或响应过期时保留现有粒度与图表数据,并通过现有错误提示机制报告失败。
🧹 Nitpick comments (6)
docs/frontend-refactor-prompt.md (1)

15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include all shared components in the documented scope.

Line 15 lists only StatusCard.vue under web/src/components/. The refactor also adds Banner.vue, CodeBlock.vue, CopyButton.vue, EmptyState.vue, Logo.vue, Panel.vue, and StatusDot.vue. Replace the explicit list with web/src/components/*.vue or enumerate these files.

Proposed documentation update
-代码位置:`web/src/views/*.vue`(7 个页面)、`web/src/components/StatusCard.vue`、`web/src/style.css`、`web/src/api/client.ts`、`web/src/router/index.ts`、`web/src/App.vue`
+代码位置:`web/src/views/*.vue`(7 个页面)、`web/src/components/*.vue`、`web/src/style.css`、`web/src/api/client.ts`、`web/src/router/index.ts`、`web/src/App.vue`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/frontend-refactor-prompt.md` at line 15, Update the documented scope in
the refactor prompt to cover all shared components under web/src/components,
replacing the StatusCard.vue-only reference with web/src/components/*.vue. Keep
the other listed paths unchanged.
web/src/style.css (2)

9-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

New --brand-*/--accent theme variables are defined but never reused; every consumer re-hardcodes the same literal colors. The shared root cause is that web/src/style.css introduces --brand-1, --brand-2, --brand-3, --brand-light, and --accent as the single source of truth for the brand palette, but no rule in either file references them — each rule repeats the equivalent hex/rgba literal instead. This defeats the purpose of centralizing the palette and risks future drift if one occurrence is updated and others are missed.

  • web/src/style.css#L9-L15: keep this as the single source of truth; convert the consumers below to reference these variables (with color-mix() or pre-defined rgba custom properties for the translucent variants) instead of repeating literals.
  • web/src/style.css#L103-L108: replace the #bd8bff, #67e8f9`` gradient stops in .page-kicker with `var(--brand-3)` and `var(--accent)`.
  • web/src/style.css#L156-L164: replace the rgba(124, 58, 237, ...), rgba(148, 85, 245, ...), and rgba(189, 139, 255, ...) values in .btn-brand with derivations of var(--brand-1), var(--brand-2), and var(--brand-3).
  • web/src/style.css#L237-L240: replace the rgba(124, 58, 237, 0.1) background in .tag-brand with a derivation of var(--brand-1).
  • web/src/App.vue#L86-L91: replace the rgba(189, 139, 255, ...), #f2e4ff, and rgba(124, 58, 237, ...) values in .nav-link.active with derivations of var(--brand-3), var(--brand-light), and var(--brand-1).
  • web/src/App.vue#L93-L103: replace the linear-gradient(90deg, #7c3aed, #67e8f9) and matching rgba(124, 58, 237, 0.8) shadow in .nav-link.active::after with var(--brand-1) and var(--accent).
  • web/src/App.vue#L105-L107: replace the #bd8bff color with var(--brand-3).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/style.css` around lines 9 - 15, Use the palette variables declared at
web/src/style.css lines 9-15 as the single source of truth. Update
web/src/style.css lines 103-108, 156-164, and 237-240 to reference --brand-1,
--brand-2, --brand-3, and --accent, deriving translucent values with color-mix()
or predefined rgba custom properties; update web/src/App.vue lines 86-91,
93-103, and 105-107 similarly for --brand-3, --brand-light, --brand-1, and
--accent, removing repeated color literals while preserving the existing visual
styling.

63-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stylelint flags on @utility and nested &:hover are false positives from an outdated syntax config.

@utility is a valid Tailwind CSS v4 directive for CSS-first custom utilities. The scss/at-rule-no-unknown and nesting-selector-no-missing-scoping-root findings at lines 63, 77, 80, and 90 come from a Stylelint config/syntax that does not recognize Tailwind v4 at-rules. If this runs in CI, it can produce noisy or blocking failures unrelated to real defects. Configure Stylelint with a Tailwind v4-aware syntax (for example, a PostCSS/Tailwind-aware customSyntax or an allowlist for @utility) so it stops flagging valid v4 syntax.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/style.css` around lines 63 - 96, Update the Stylelint configuration
used for web/src/style.css to recognize Tailwind CSS v4 syntax, including the
`@utility` at-rule and nested &:hover selectors. Use the project’s supported
PostCSS/Tailwind-aware custom syntax or allowlist `@utility` so these valid
declarations no longer produce CI-blocking false positives.

Source: Linters/SAST tools

web/src/components/Banner.vue (1)

14-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add ARIA live-region semantics to the banner.

The banner div has no role or aria-live. Screen readers may not announce the message when it appears after an action (for example, a failed save). Add role="alert" (or aria-live="assertive") for the error tone, and role="status" (or aria-live="polite") for success/info.

♻️ Proposed fix
   <div
     v-if="message || $slots.default"
     class="mb-4 flex items-start gap-2.5 rounded-md border px-3 py-2 text-sm"
+    role="status"
+    :aria-live="tone === 'error' ? 'assertive' : 'polite'"
     :class="{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/Banner.vue` around lines 14 - 23, Update the banner div in
the Banner component to expose live-region semantics based on tone: use
alert/assertive behavior for error messages and status/polite behavior for
success and info messages, while preserving the existing conditional rendering
and styling.
web/src/components/StatusDot.vue (1)

11-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated tone-to-color maps into a single computed value.

The pulse ring and the solid dot each embed their own tone-keyed color map inline in :class. Adding or renaming a tone requires updating two maps and the prop type in sync.

♻️ Proposed refactor
 <script setup lang="ts">
+import { computed } from 'vue'
+
 withDefaults(defineProps<{
   tone?: 'ok' | 'warn' | 'danger' | 'off' | 'brand' | 'accent'
   pulse?: boolean
 }>(), {
   tone: 'off',
   pulse: false,
 })
+
+const props = withDefaults(defineProps<{ ... }>(), { ... })
+const ringClass = computed(() => ({
+  ok: 'bg-emerald-400/60', warn: 'bg-amber-400/60', danger: 'bg-red-400/60',
+  off: 'bg-slate-500/40', brand: 'bg-violet-400/60', accent: 'bg-cyan-400/60',
+}[props.tone]))
+const dotClass = computed(() => ({
+  ok: 'bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.8)]',
+  warn: 'bg-amber-400 shadow-[0_0_8px_rgba(251,191,36,0.8)]',
+  danger: 'bg-red-400 shadow-[0_0_8px_rgba(248,113,113,0.8)]',
+  off: 'bg-slate-600 shadow-[0_0_6px_rgba(71,85,105,0.6)]',
+  brand: 'bg-violet-400 shadow-[0_0_8px_rgba(167,139,250,0.8)]',
+  accent: 'bg-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.8)]',
+}[props.tone]))
 </script>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/StatusDot.vue` around lines 11 - 38, Extract the
duplicated tone-to-color mappings used by the pulse ring and solid dot in
StatusDot.vue into a single computed tone-class value. Update both :class
bindings to consume that shared value while preserving each tone’s existing
colors and pulse behavior.
web/src/components/StatusCard.vue (1)

4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the any prop type with a proper Mirror interface.

mirror: any discards type checking for every field accessed in the template (name, status, enabled, pattern, upstream, error). Import or define a Mirror type (likely already used by the API client that feeds this data) and use it here instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/StatusCard.vue` at line 4, Replace the any type in
StatusCard’s defineProps mirror declaration with the existing Mirror interface,
importing it from the API client or defining it if unavailable. Ensure the type
includes the template-accessed fields name, status, enabled, pattern, upstream,
and error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/frontend-refactor-prompt.md`:
- Line 64: Update the visual-validation step around the local dev startup
command to provide a Windows-compatible PowerShell command, or explicitly state
that the existing nohup and /tmp/dev.log command must run in Git Bash. Keep the
requirement to run the dev server in the background, capture logs, perform
screenshot-based checks, and stop the process afterward.
- Around line 43-44: 更新设计方向条目 1 和 2,明确采用 liquid-glass
液态玻璃风格作为目标,同时保留暗色终端极客基调、统一组件视觉语言及紫青品牌色系统。删除或改写可能引导后续工作恢复旧视觉语言的表述,并确保文本与
AGENTS.md 及 PR 目标一致。

In `@web/src/components/CopyButton.vue`:
- Around line 18-32: Update the `copy` function to track whether the fallback
`document.execCommand('copy')` call succeeds, and only set `copied.value` to
true and start the reset timer when copying succeeds. Preserve the existing
primary clipboard path and textarea cleanup behavior.

In `@web/src/views/Dashboard.vue`:
- Around line 216-225: Update the peak-point rendering near the chart circle and
reuse bigChartPoints’s normalized source and coordinate calculations for both cx
and cy. Ensure the single-bucket case uses the same [0, value] fallback so cx
never becomes NaN, and place the circle at the actual peak coordinates instead
of the fixed cy value.
- Around line 142-153: Update the trendStats computed property to calculate
active mirror count from the complete, untruncated aggregation data rather than
trendRows. Keep slice(0, 8) limited to the table-rendering data so
trendStats.active reflects all active mirrors.

In `@web/src/views/Mirrors.vue`:
- Around line 109-111: 避免同一次编辑重复调用 updateUpstream:调整模板中 `@change` 和 `@keydown.enter`
的事件绑定,保留单一保存入口,或让 Enter 仅触发输入框失焦并由 change 统一执行保存,确保配置 API 每次编辑只收到一个请求。

In `@web/src/views/Search.vue`:
- Line 168: Update the EmptyState condition at line 168 in the Search.vue
component to use the normalized query value. Replace the direct `query`
reference in the v-if condition with `query.trim()` to ensure that queries
containing only whitespace are not treated as valid searches. This prevents the
"no results" message from appearing when the user enters only spaces, since
doSearch does not execute API requests for such inputs.

---

Outside diff comments:
In `@web/src/views/Dashboard.vue`:
- Around line 177-181: 更新 switchGranularity,为每次请求分配递增请求 ID
或取消前一个请求,并仅允许最新请求成功后同时更新 chartGranularity 和
hourlyTraffic;请求失败或响应过期时保留现有粒度与图表数据,并通过现有错误提示机制报告失败。

---

Nitpick comments:
In `@docs/frontend-refactor-prompt.md`:
- Line 15: Update the documented scope in the refactor prompt to cover all
shared components under web/src/components, replacing the StatusCard.vue-only
reference with web/src/components/*.vue. Keep the other listed paths unchanged.

In `@web/src/components/Banner.vue`:
- Around line 14-23: Update the banner div in the Banner component to expose
live-region semantics based on tone: use alert/assertive behavior for error
messages and status/polite behavior for success and info messages, while
preserving the existing conditional rendering and styling.

In `@web/src/components/StatusCard.vue`:
- Line 4: Replace the any type in StatusCard’s defineProps mirror declaration
with the existing Mirror interface, importing it from the API client or defining
it if unavailable. Ensure the type includes the template-accessed fields name,
status, enabled, pattern, upstream, and error.

In `@web/src/components/StatusDot.vue`:
- Around line 11-38: Extract the duplicated tone-to-color mappings used by the
pulse ring and solid dot in StatusDot.vue into a single computed tone-class
value. Update both :class bindings to consume that shared value while preserving
each tone’s existing colors and pulse behavior.

In `@web/src/style.css`:
- Around line 9-15: Use the palette variables declared at web/src/style.css
lines 9-15 as the single source of truth. Update web/src/style.css lines
103-108, 156-164, and 237-240 to reference --brand-1, --brand-2, --brand-3, and
--accent, deriving translucent values with color-mix() or predefined rgba custom
properties; update web/src/App.vue lines 86-91, 93-103, and 105-107 similarly
for --brand-3, --brand-light, --brand-1, and --accent, removing repeated color
literals while preserving the existing visual styling.
- Around line 63-96: Update the Stylelint configuration used for
web/src/style.css to recognize Tailwind CSS v4 syntax, including the `@utility`
at-rule and nested &:hover selectors. Use the project’s supported
PostCSS/Tailwind-aware custom syntax or allowlist `@utility` so these valid
declarations no longer produce CI-blocking false positives.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3292812f-bb43-4ffb-b857-ba457e102feb

📥 Commits

Reviewing files that changed from the base of the PR and between 3debfd8 and a180eff.

📒 Files selected for processing (19)
  • AGENTS.md
  • docs/frontend-refactor-prompt.md
  • web/src/App.vue
  • web/src/components/Banner.vue
  • web/src/components/CodeBlock.vue
  • web/src/components/CopyButton.vue
  • web/src/components/EmptyState.vue
  • web/src/components/Logo.vue
  • web/src/components/Panel.vue
  • web/src/components/StatusCard.vue
  • web/src/components/StatusDot.vue
  • web/src/style.css
  • web/src/views/Dashboard.vue
  • web/src/views/GitProxy.vue
  • web/src/views/Login.vue
  • web/src/views/Mirrors.vue
  • web/src/views/Releases.vue
  • web/src/views/Search.vue
  • web/src/views/Settings.vue

Comment thread docs/frontend-refactor-prompt.md Outdated
Comment thread docs/frontend-refactor-prompt.md Outdated
Comment thread web/src/components/CopyButton.vue
Comment thread web/src/views/Dashboard.vue
Comment thread web/src/views/Dashboard.vue
Comment thread web/src/views/Mirrors.vue Outdated
Comment thread web/src/views/Search.vue Outdated
wha7ev9r added 2 commits July 31, 2026 16:20
- Fix granularity switch race: track latest request id, ignore stale responses
- Fix traffic peak marker: compute cx/cy from data (no NaN on single bucket)
- Count active mirrors from full data, not top-8 table rows
- Localize remaining English fallback strings
- CopyButton: only show copied state when fallback copy succeeds
- Mirrors: Enter blurs input so change handles save once (no double request)
- Search: trim query for empty-state conditions
- Banner: add role/aria-live semantics
- StatusDot: extract tone maps into computed values
- StatusCard: replace any with Mirror interface
- Use palette CSS vars (--brand-*/--accent) with color-mix in glass/buttons/nav
- Remove docs/ plan file from tracking; update view.png preview
- AGENTS.md/docs: document liquid-glass direction
@wha7ev9r
wha7ev9r merged commit 2d0b2ef into main Jul 31, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant