Conversation
This PR addresses multiple quality and security issues identified in code reviews (e.g., CodeRabbit), focusing on hardening components and resolving async race conditions. Changes Included: - `post-reactions.tsx`: Addressed race condition in `setTimeout` using functional state update check when unsetting the animating state. - `share-buttons.tsx`: Prevented timeout leaks and state override by capturing the timer with `useRef` and ensuring it properly clears across consecutive clicks or unmount scenarios. - `dashboard.tsx`: Hardened `.toggleSelect` logic by applying a functional state updater `prev => ...` to prevent loss of state on fast consecutive clicks. - `globals.css`: Corrected the fallback styling for the `.reading-mode-exit-fab` button under light mode by replacing `:root:not(.dark)` with an explicit `[data-theme="light"]` selector to prevent unexpected overrides.
1. 在路由切换时强制判断 Hash 并重置滚动条到顶部 window.scrollTo(0,0),解决用户反馈的点进新文章后直接展示尾部评论区的诡异体验。 2. 将使用 marked 与 highlight.js 解析高亮的大块 Markdown 任务解耦,从同步的 useMemo 移至 setTimeout 中推迟至下一个事件循环执行,彻底解除繁重字符串解析对主线程首次绘制的阻塞,大幅度优化首页跳文章页的“首帧感知加载速度”(Perceived Performance)。
为博客打造的全权管理 MCP 服务器,共 30 个工具: - 📝 文章管理 (8): list/get/create/update/delete/batch/search/versions - 💬 评论管理 (3): list/approve/delete - 🖼️ 媒体管理 (3): list/upload/delete - 📊 统计分析 (3): dashboard_stats/analytics/traffic - ⚙️ 站点设置 (2): get/update - 📄 独立页面 (4): list/get/upsert/delete - 🏷️ 分类标签 (3): tags/categories/series - 💾 备份恢复 (4): export/backup_to_r2/list_r2/restore 技术栈:@modelcontextprotocol/sdk + Zod + stdio 传输 安全防线:JWT 自动认证、高危操作标注、恢复前自动快照
marked v15+ 中 table renderer 的 token.header 和 token.rows 不再是 HTML 字符串,而是嵌套 Token 数组。旧代码直接拼接导致渲染为 [object Object]。 修复方案:改用 function 声明(而非箭头函数),通过 this.parser.parseInline() 递归解析每个 cell 的 tokens,正确生成 HTML 表格。
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 20 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (14)
📝 WalkthroughWalkthrough本PR修复客户端组件超时竞态、重构Markdown渲染器以适配 marked v15+、调整全局样式与主题选择器,并新增一个独立的 MCP 服务器子项目(含HTTP客户端、JWT认证与一系列工具注册)。 Changes
Sequence Diagram(s)sequenceDiagram
participant Tool as MCP Tool
participant Client as HTTP Client<br/>(mcp-server/src/client.ts)
participant Auth as Token Cache
participant API as Monolith API
Tool->>Client: apiRequest(path, options)
alt token missing or expiring
Client->>Auth: check cachedToken/tokenExpiresAt
Auth-->>Client: no valid token
Client->>API: POST /api/auth/login { password }
API-->>Client: { token }
Client->>Auth: store token & expiry (refresh 1h before)
else token valid
Auth-->>Client: return token
end
Client->>API: request (with Authorization if auth !== false)
API-->>Client: response (JSON/text)
Client->>Tool: return parsed result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@mcp-server/src/client.ts`:
- Around line 39-42: Avoid hardcoding a 7-day TTL: when you set cachedToken and
tokenExpiresAt after (await res.json()) use the token's exp claim to derive
tokenExpiresAt (decode JWT payload and set tokenExpiresAt = exp - safetyMargin)
and only fall back to a server-provided TTL or a short default if exp is absent;
also implement a 401 retry path that clears cachedToken and forces
re-authentication once before failing. Update the logic around cachedToken,
tokenExpiresAt and the response handling from res.json() to decode the JWT,
compute expiry from the exp field with a safety cushion (e.g., 1 hour), and
ensure on 401 the client clears cachedToken and retries the request once to
obtain a fresh token.
- Around line 28-32: The fetch call used for login (the POST to
`${apiUrl}/api/auth/login` in mcp-server/src/client.ts) lacks timeout/abort
handling; implement a reusable fetch-with-timeout wrapper (e.g.,
fetchWithTimeout) that creates an AbortController, passes its signal to fetch,
and calls controller.abort() via setTimeout when the configured timeout elapses,
then use this wrapper for the login request and the other generic requests
referenced around lines 94-98; ensure the timeout is configurable, that you
clear the timer on success to avoid leaks, and propagate/translate abort errors
appropriately so callers can handle timeouts.
In `@mcp-server/src/tools/analytics.ts`:
- Line 31: The Zod schema for the days field currently allows any number; update
the days validator in the analytics schema (the days field in the Zod object) to
enforce an integer range matching the backend (e.g., .int().min(1).max(90))
while keeping the .default(30) and .describe(...) so the frontend rejects
zero/negative/too-large values consistently with the backend.
In `@mcp-server/src/tools/backup.ts`:
- Around line 73-80: The code currently continues with the destructive restore
even if the pre-restore snapshot (apiRequest("/api/admin/backup/r2")) fails;
update the restore flow (the restore_backup handler in
mcp-server/src/tools/backup.ts) so that when the snapshot creation in the try
block throws, you do not proceed—rethrow or return an error and fail the restore
operation instead of logging and continuing; ensure the error from apiRequest is
propagated (or cause process exit) so callers of restore_backup can detect and
abort the destructive restore.
- Around line 17-21: The code currently returns a truncated, non-JSON "backup"
when text.length > 50000; change this to explicitly fail instead of returning
corrupted data: detect the oversized payload where the variable truncated is
computed and throw or return a clear error/exception (e.g., new Error or a
structured error response) that says the backup exceeds 50,000 chars and
instructs the caller to use the backup_to_r2 tool for full export; do not
produce or return any truncated string so restore_backup consumers only ever
receive valid, complete JSON or an explicit failure.
In `@mcp-server/src/tools/media.ts`:
- Around line 42-43: The upload code duplicates a weaker auth flow by using
getConfigForUpload() and getTokenForUpload() that diverge from the shared logic
in mcp-server/src/client.ts (missing env defaulting, no res.ok checks, hardcoded
token TTL), so replace/align the upload path to reuse the existing auth and
error handling: remove or stop using getConfigForUpload/getTokenForUpload and
instead call the shared getConfig/getToken from client.ts (or extend apiRequest
to accept FormData and use apiRequest for uploads) so uploads inherit the same
env fallback, res.ok checks, TTL handling and consistent error logging; ensure
references to getConfigForUpload, getTokenForUpload and the upload call are
updated to use apiRequest or the shared getToken/getConfig.
- Around line 82-85: The delete-media call builds the URL with raw key which can
contain slashes and break routing; in the async handler that accepts ({ key })
and calls apiRequest(`/api/admin/media/${key}`, { method: "DELETE", ... })
replace the interpolation with an encoded key using encodeURIComponent(key) so
the request path becomes `/api/admin/media/${encodeURIComponent(key)}`; update
any other places in this module using the same pattern (search for
`/api/admin/media/${key}` and apiRequest calls) to consistently encode keys
before sending.
- Around line 51-55: The upload_media function is calling fetch directly without
timeout/abort (the fetch to `${apiUrl}/api/admin/upload` with headers
Authorization and body formData), so add an AbortController and a timeout timer
(e.g., 30s or configurable) and pass controller.signal to fetch; clear the timer
on completion and call controller.abort() on timeout and surface a clear timeout
error so the caller won’t hang; ensure the same error handling path used by
apiRequest is applied (or normalize the thrown error) so token/apiUrl logic
stays unchanged.
In `@mcp-server/src/tools/pages.ts`:
- Around line 30-33: The code is directly interpolating slug into the request
path (inside the async handler that calls
apiRequest(`/api/admin/pages/${slug}`)), which can cause misrouting for special
characters; update the call to encode the slug (e.g., use encodeURIComponent or
build the URL via the URL API) before concatenation so apiRequest receives a
safe, encoded path (refer to the async ({ slug }) => { ... } handler and the
apiRequest invocation).
- Around line 70-77: The "delete_page" command currently accepts only { slug }
and calls apiRequest directly; add a required confirm boolean to the input
schema and perform a hard check in the handler (e.g., change the handler
signature from async ({ slug }) to async ({ slug, confirm }) and validate
confirm === true) before calling apiRequest to prevent accidental deletes;
update the zod schema used in this command to include confirm: z.literal(true)
or z.boolean().refine(v => v === true) so requests without explicit confirmation
are rejected, and return/throw an explicit error if confirm is missing or false.
In `@mcp-server/src/tools/posts.ts`:
- Around line 30-32: The code concatenates raw slug values into request paths
(e.g., in the handler passed to the zod schema where
apiRequest(`/api/posts/${slug}`) is called), which breaks for characters like /,
?, % or non-ASCII; update every place that builds paths with a slug (including
functions/handlers for update_post, delete_post, list_post_versions and the call
sites at the indicated ranges) to pass encodeURIComponent(slug) into the URL
construction (e.g., apiRequest(`/api/posts/${encodeURIComponent(slug)}`, ...));
optionally add input validation to enforce a safe slug format at the zod schema
level to prevent invalid characters.
In `@mcp-server/src/tools/settings.ts`:
- Line 33: The posts_per_page schema currently uses posts_per_page:
z.number().optional() which allows negatives, zero, floats and huge values;
update the schema for the posts_per_page field to enforce integer and bounds
(e.g., use z.number().int().min(1).max(100).optional() or similar limits
appropriate for your app) so only positive integers within a sensible range are
allowed; locate the posts_per_page entry in the settings schema in settings.ts
and replace the z.number().optional() call with zod integer and range
validators.
In `@mcp-server/src/tools/taxonomy.ts`:
- Around line 46-49: 在调用 apiRequest 时直接插入 slug 会导致包含 / ? # 等字符时破坏 URL
语义;在构造路径(当前在异步回调中对 `/api/series/${slug}` 的拼接)前用 encodeURIComponent(slug) 对 slug
进行 URL 编码,然后把编码后的值拼入路径,保留原有 auth: false 行为以确保请求权限不变(定位符:slug, apiRequest,
series)。
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bc2ebb9c-fbeb-4e19-934a-efe3b917ac5e
⛔ Files ignored due to path filters (1)
mcp-server/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (19)
client/src/components/post-reactions.tsxclient/src/components/share-buttons.tsxclient/src/globals.cssclient/src/lib/markdown.tsclient/src/pages/admin/dashboard.tsxclient/src/pages/post.tsxmcp-server/package.jsonmcp-server/src/client.tsmcp-server/src/index.tsmcp-server/src/tools/analytics.tsmcp-server/src/tools/backup.tsmcp-server/src/tools/comments.tsmcp-server/src/tools/media.tsmcp-server/src/tools/pages.tsmcp-server/src/tools/posts.tsmcp-server/src/tools/settings.tsmcp-server/src/tools/taxonomy.tsmcp-server/src/types.tsmcp-server/tsconfig.json
👮 Files not reviewed due to content moderation or server errors (2)
- client/src/lib/markdown.ts
- client/src/pages/post.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
client/src/components/**
⚙️ CodeRabbit configuration file
client/src/components/**: 这是 React 前端组件目录。审查时请关注: 1. 是否同时兼容暗色和亮色主题(检查 CSS 变量和 data-theme) 2. 响应式布局是否完整(移动端/平板/桌面端) 3. 无障碍访问(aria 标签、键盘导航) 4. 组件是否保持单一职责
Files:
client/src/components/post-reactions.tsxclient/src/components/share-buttons.tsx
client/src/globals.css
⚙️ CodeRabbit configuration file
client/src/globals.css: 全局样式和 CSS 变量系统。审查时请关注: 1. [data-theme="light"] 和默认暗色主题的变量是否配对 2. OKLCH 色值的明度/色度是否合理 3. 是否有遗漏的选择器未覆盖亮色模式
Files:
client/src/globals.css
client/src/pages/**
⚙️ CodeRabbit configuration file
client/src/pages/**: 页面级组件。审查时请关注: 1. 数据加载和错误处理是否完善 2. SEO 相关(页面标题、meta 标签) 3. 导航和路由是否正确
Files:
client/src/pages/admin/dashboard.tsxclient/src/pages/post.tsx
🔇 Additional comments (10)
client/src/pages/admin/dashboard.tsx (1)
85-90: 状态更新方式改进正确,避免闭包陈旧值问题。这里改为函数式
setSelectedSlugs((prev) => ...)后,快速连续点击时也能基于最新状态计算,Set拷贝与增删逻辑也保持了不可变更新语义,整体实现没问题。client/src/components/post-reactions.tsx (1)
53-56: 竞态修复实现正确,动画状态回写更安全。Line 53-56 通过捕获
clickedType并使用函数式setAnimating,仅在当前动画类型一致时清空状态,能避免快速点击时旧定时器覆盖新动画状态。client/src/components/share-buttons.tsx (2)
2-2: Hook 导入调整合理。新增
useRef与useEffect与后续计时器生命周期管理一致,方向正确。
25-25: 复制状态计时器的竞态与清理处理到位。先清理旧 timeout 再创建新 timeout,并在卸载时统一清理,避免了快速重复点击和组件卸载后的计时器残留问题。
Also applies to: 31-32, 36-40
client/src/globals.css (1)
1520-1524: 亮色阅读退出按钮选择器调整正确,双主题覆盖完整。Line 1520 和 Line 1526 改为
[data-theme="light"]后与当前主题注入方式一致,且亮色下基础态与 hover 态都已覆盖,未见回归风险。
As per coding guidelinesclient/src/globals.css: “审查时请关注: 1. [data-theme="light"] 和默认暗色主题的变量是否配对 2. OKLCH 色值的明度/色度是否合理 3. 是否有遗漏的选择器未覆盖亮色模式”。Also applies to: 1526-1530
mcp-server/package.json (1)
7-19: 配置完整且与当前实现匹配。脚本、运行时依赖与开发依赖声明清晰,能够支撑当前 MCP 服务的构建与启动流程。
mcp-server/tsconfig.json (1)
3-18: TS 编译配置合理。
strict、Node16模块解析与声明文件输出的组合较稳健,适合该 MCP 服务的类型安全与发布需求。mcp-server/src/types.ts (1)
6-75: 共享类型拆分清晰,利于后续维护。将核心实体类型集中定义后,工具模块之间的数据边界更明确,整体可读性和可维护性都更好。
mcp-server/src/index.ts (1)
23-73: 入口编排结构清楚。工具注册顺序、stdio 启动和顶层异常处理都比较完整,作为 MCP 入口实现是稳妥的。
mcp-server/src/tools/comments.ts (1)
11-64: 实现比较稳。三个工具的参数定义、请求封装和返回结构都比较一致,可读性也不错。
| "get_analytics", | ||
| "获取详细的访问分析数据,可指定时间范围", | ||
| { | ||
| days: z.number().default(30).describe("查询最近多少天的数据"), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "analytics.ts" -type fRepository: one-ea/Monolith
Length of output: 94
🏁 Script executed:
git ls-files | grep -E "(analytics|tool)" | head -20Repository: one-ea/Monolith
Length of output: 349
🏁 Script executed:
cat -n mcp-server/src/tools/analytics.ts | head -50Repository: one-ea/Monolith
Length of output: 1580
🏁 Script executed:
# Search for any backend constraints or documentation on days parameter
rg "days" mcp-server/src/tools/analytics.ts -A 5 -B 5Repository: one-ea/Monolith
Length of output: 457
🏁 Script executed:
# Check if there are other usages of this analytics tool
rg "get_analytics\|days.*parameter\|days.*constraint" --type ts --type js -iRepository: one-ea/Monolith
Length of output: 41
🏁 Script executed:
# Look for any API documentation or constants related to days limits
rg "365\|30.*day\|days.*limit\|max.*day" --type ts --type js -i | head -30Repository: one-ea/Monolith
Length of output: 41
🏁 Script executed:
# Search for the backend API handler
rg "admin/analytics" --type ts --type jsRepository: one-ea/Monolith
Length of output: 681
🏁 Script executed:
# Look for API route handlers or backend logic
fd -e ts -e js | xargs grep -l "analytics\|days" | grep -E "(api|route|handler)" | head -10Repository: one-ea/Monolith
Length of output: 80
🏁 Script executed:
# Check other tools to see if they have similar bounds validation
rg "\.min\(|\.max\(" mcp-server/src/tools/ -A 1 -B 1Repository: one-ea/Monolith
Length of output: 299
🏁 Script executed:
# Look for any configuration or constants related to data retention
rg "retention\|archive\|history\|window" --type ts --type js -i | head -20Repository: one-ea/Monolith
Length of output: 41
🏁 Script executed:
# Check the backend API handler for days parameter constraints
cat -n server/src/index.ts | grep -A 30 'app.get("/api/admin/analytics"'Repository: one-ea/Monolith
Length of output: 1260
🏁 Script executed:
# Look for the full analytics handler implementation
rg "app.get\(\"/api/admin/analytics" -A 50 server/Repository: one-ea/Monolith
Length of output: 2793
🏁 Script executed:
# Check if there's a database query or schema that limits the data window
rg "SELECT.*analytics\|query.*days\|WHERE.*days" --type ts --type js -iRepository: one-ea/Monolith
Length of output: 41
🏁 Script executed:
# Check client-side API wrapper to see if there are any constraints
cat -n client/src/lib/api.ts | head -100Repository: one-ea/Monolith
Length of output: 3705
🏁 Script executed:
# Double-check the getAnalytics method signature to confirm 90 is the intended limit
rg "getAnalytics" server/ -A 10Repository: one-ea/Monolith
Length of output: 4197
调整 days 参数校验范围与后端一致。
当前参数缺少范围校验,负数、零或超大值都可能被接受,导致不必要的数据库扫描。后端已明确限制在 90 天内(见 Math.min(days, 90)),前端应同步校验以确保类型安全。
🔧 建议修改
- days: z.number().default(30).describe("查询最近多少天的数据"),
+ days: z.number().int().min(1).max(90).default(30).describe("查询最近多少天的数据(1-90)"),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| days: z.number().default(30).describe("查询最近多少天的数据"), | |
| days: z.number().int().min(1).max(90).default(30).describe("查询最近多少天的数据(1-90)"), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mcp-server/src/tools/analytics.ts` at line 31, The Zod schema for the days
field currently allows any number; update the days validator in the analytics
schema (the days field in the Zod object) to enforce an integer range matching
the backend (e.g., .int().min(1).max(90)) while keeping the .default(30) and
.describe(...) so the frontend rejects zero/negative/too-large values
consistently with the backend.
| const text = typeof backup === 'string' ? backup : JSON.stringify(backup, null, 2); | ||
| // 截断过长输出避免 MCP 消息超限 | ||
| const truncated = text.length > 50000 | ||
| ? text.slice(0, 50000) + "\n\n... [数据已截断,完整备份请通过 backup_to_r2 工具推送到云端]" | ||
| : text; |
There was a problem hiding this comment.
不要返回被截断的“备份 JSON”。
这里一旦超过 50,000 字符,就会返回一个被截断的字符串;它既不是完整备份,也不是合法 JSON,后续无法直接复用到 restore_backup。工具描述是“导出全部数据”,更稳妥的行为是显式报错并引导改用 backup_to_r2,而不是静默返回损坏结果。
💡 建议改法
const backup = await apiRequest("/api/admin/backup/export");
const text = typeof backup === 'string' ? backup : JSON.stringify(backup, null, 2);
- // 截断过长输出避免 MCP 消息超限
- const truncated = text.length > 50000
- ? text.slice(0, 50000) + "\n\n... [数据已截断,完整备份请通过 backup_to_r2 工具推送到云端]"
- : text;
+ if (text.length > 50000) {
+ return {
+ content: [{
+ type: "text" as const,
+ text: "❌ 备份数据过大,无法通过 MCP 直接返回完整 JSON。请改用 backup_to_r2 创建云端快照。",
+ }],
+ isError: true,
+ };
+ }
return {
content: [{
type: "text" as const,
- text: truncated,
+ text,
}],
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const text = typeof backup === 'string' ? backup : JSON.stringify(backup, null, 2); | |
| // 截断过长输出避免 MCP 消息超限 | |
| const truncated = text.length > 50000 | |
| ? text.slice(0, 50000) + "\n\n... [数据已截断,完整备份请通过 backup_to_r2 工具推送到云端]" | |
| : text; | |
| const backup = await apiRequest("/api/admin/backup/export"); | |
| const text = typeof backup === 'string' ? backup : JSON.stringify(backup, null, 2); | |
| if (text.length > 50000) { | |
| return { | |
| content: [{ | |
| type: "text" as const, | |
| text: "❌ 备份数据过大,无法通过 MCP 直接返回完整 JSON。请改用 backup_to_r2 创建云端快照。", | |
| }], | |
| isError: true, | |
| }; | |
| } | |
| return { | |
| content: [{ | |
| type: "text" as const, | |
| text, | |
| }], | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mcp-server/src/tools/backup.ts` around lines 17 - 21, The code currently
returns a truncated, non-JSON "backup" when text.length > 50000; change this to
explicitly fail instead of returning corrupted data: detect the oversized
payload where the variable truncated is computed and throw or return a clear
error/exception (e.g., new Error or a structured error response) that says the
backup exceeds 50,000 chars and instructs the caller to use the backup_to_r2
tool for full export; do not produce or return any truncated string so
restore_backup consumers only ever receive valid, complete JSON or an explicit
failure.
| { slug: z.string().describe("系列 slug") }, | ||
| async ({ slug }) => { | ||
| const series = await apiRequest(`/api/series/${slug}`, { auth: false }); | ||
| return { |
There was a problem hiding this comment.
请对 slug 进行 URL 编码后再拼接路径。
当前直接插值 slug,当输入包含 /、?、# 等字符时会改变请求语义,存在误路由风险。建议在 Line [48] 使用 encodeURIComponent(slug)。
🔧 建议修改
- const series = await apiRequest(`/api/series/${slug}`, { auth: false });
+ const series = await apiRequest(`/api/series/${encodeURIComponent(slug)}`, { auth: false });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { slug: z.string().describe("系列 slug") }, | |
| async ({ slug }) => { | |
| const series = await apiRequest(`/api/series/${slug}`, { auth: false }); | |
| return { | |
| { slug: z.string().describe("系列 slug") }, | |
| async ({ slug }) => { | |
| const series = await apiRequest(`/api/series/${encodeURIComponent(slug)}`, { auth: false }); | |
| return { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mcp-server/src/tools/taxonomy.ts` around lines 46 - 49, 在调用 apiRequest 时直接插入
slug 会导致包含 / ? # 等字符时破坏 URL 语义;在构造路径(当前在异步回调中对 `/api/series/${slug}` 的拼接)前用
encodeURIComponent(slug) 对 slug 进行 URL 编码,然后把编码后的值拼入路径,保留原有 auth: false
行为以确保请求权限不变(定位符:slug, apiRequest, series)。
marked v15 传入 renderer 的 text 参数是原始 Markdown 文本(如 **bold**), 而非已解析的 HTML。heading 和 link renderer 都需要改用 function 声明 (获取 this 绑定),通过 this.parser.parseInline(tokens) 正确渲染 粗体、代码、斜体等 inline 格式。 修复概要: - heading: 箭头函数 → function,用 parseInline 生成 HTML 内容 - link: 同上,确保链接文本中的格式被正确渲染 - table: 前一个 commit 已修复
变更要点: - 行高从 1.7 提升到 1.85,代码阅读更透气 - 新增 .code-header 顶部信息栏:语言标签左对齐、复制按钮右对齐 - 取消复制按钮的 Tailwind 绝对定位,改用纯 CSS flex 布局 - 有 title 时复制按钮移入标题栏右侧 - 无语言标识时默认显示 'CODE' 标签 - 同步亮色模式覆盖样式
排版参数调整: - 正文行高 1.8→1.9,字间距 0→0.012em,增加呼吸感 - 段间距 16px→20px,段落节奏更从容 - h2 顶距 40→48px,h3 顶距 32→36px,章节分隔更明晰 - 列表行间距增加,列表项行高独立设为 1.8 - 引用块/表格上下间距统一加大 块间衔接优化: - 标题后紧接代码块/表格/引用时缩小间距(16px),避免割裂 - 连续代码块间距缩小(16px),保持视觉连贯 - 代码块 letter-spacing 强制为 0(不受正文字间距影响) - 统一用 .code-block-wrapper margin 管理代码块间距
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@client/src/globals.css`:
- Around line 431-438: The CSS currently only resets margin-top for the code
block's pre element, leaving margin-bottom and causing doubled spacing because
.prose-monolith .code-block-wrapper already provides margin: 24px 0; update the
rule for .prose-monolith .code-block-wrapper pre to clear all margins (e.g.,
reset margin to 0) so the wrapper exclusively controls vertical spacing; target
the selector .prose-monolith .code-block-wrapper pre to make this change so both
markdown code-paths in client/src/lib/markdown.ts use the corrected layout.
- Around line 397-407: The CSS rule for .prose-monolith code:not(pre code) uses
the deprecated property word-break: break-word; replace that declaration with
overflow-wrap: anywhere to achieve the same wrapping behavior in modern
browsers; locate the selector .prose-monolith code:not(pre code) and swap out
the word-break line for overflow-wrap: anywhere (keeping the surrounding
padding, font, background, border and color declarations intact).
In `@client/src/lib/markdown.ts`:
- Around line 219-240: The header/body table cell rendering falls back to
String(cell) which can produce “[object Object]” for unexpected token shapes; in
the token.header handling (building headerHtml) and token.rows mapping (building
bodyHtml) for each cell, change the fallback from String(cell) to an empty
string so when neither cell.tokens nor a string cell.text exist you return ""
instead; update the two locations where cellText is computed (the header map and
the row map that call this.parser.parseInline or check typeof cell.text) to use
"" as the safe default.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 41565989-f70e-4ac0-aa02-9222f3b24264
📒 Files selected for processing (2)
client/src/globals.cssclient/src/lib/markdown.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
client/src/globals.css
⚙️ CodeRabbit configuration file
client/src/globals.css: 全局样式和 CSS 变量系统。审查时请关注: 1. [data-theme="light"] 和默认暗色主题的变量是否配对 2. OKLCH 色值的明度/色度是否合理 3. 是否有遗漏的选择器未覆盖亮色模式
Files:
client/src/globals.css
🪛 Stylelint (17.6.0)
client/src/globals.css
[error] 406-406: Unexpected deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
🔇 Additional comments (1)
client/src/lib/markdown.ts (1)
63-69: heading的 id 生成应与 TOC 提取逻辑保持一致。当前第64行对原始
text直接slugify,而同文件extractHeadings在第315-319行先剥离链接、代码等 markdown 语法再slugify。两个函数应使用相同的文本处理逻辑。建议:确保
renderer.heading的文本处理与extractHeadings一致,而非使用html生成 id(html包含HTML标签,作为id基础不恰当)。可考虑将纯文本处理逻辑统一提取为单独函数。
| /* 代码块包裹容器间距(统一管理 margin,pre 自身 margin 被 has-header 覆盖为 0) */ | ||
| .prose-monolith .code-block-wrapper { | ||
| margin: 24px 0; | ||
| } | ||
|
|
||
| .prose-monolith .code-block-wrapper pre { | ||
| margin-top: 0; | ||
| } |
There was a problem hiding this comment.
包裹容器接管间距时,内部 pre 需要把外边距全部清零。
现在只清掉了 margin-top,但 pre 仍然保留 margin-bottom: 24px。结合 .code-block-wrapper { margin: 24px 0; },实际底部间距会变成 48px。client/src/lib/markdown.ts:157-166 的两条代码块分支都会走这个结构,所以这个偏差会稳定出现。
可选修正
.prose-monolith .code-block-wrapper pre {
- margin-top: 0;
+ margin: 0;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /* 代码块包裹容器间距(统一管理 margin,pre 自身 margin 被 has-header 覆盖为 0) */ | |
| .prose-monolith .code-block-wrapper { | |
| margin: 24px 0; | |
| } | |
| .prose-monolith .code-block-wrapper pre { | |
| margin-top: 0; | |
| } | |
| /* 代码块包裹容器间距(统一管理 margin,pre 自身 margin 被 has-header 覆盖为 0) */ | |
| .prose-monolith .code-block-wrapper { | |
| margin: 24px 0; | |
| } | |
| .prose-monolith .code-block-wrapper pre { | |
| margin: 0; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/globals.css` around lines 431 - 438, The CSS currently only resets
margin-top for the code block's pre element, leaving margin-bottom and causing
doubled spacing because .prose-monolith .code-block-wrapper already provides
margin: 24px 0; update the rule for .prose-monolith .code-block-wrapper pre to
clear all margins (e.g., reset margin to 0) so the wrapper exclusively controls
vertical spacing; target the selector .prose-monolith .code-block-wrapper pre to
make this change so both markdown code-paths in client/src/lib/markdown.ts use
the corrected layout.
| // 计算标签频次并按热度排序 | ||
| const tagCounts = posts.flatMap((p) => p.tags).reduce<Record<string, number>>((acc, t) => { | ||
| if (t === "__proto__" || t === "constructor") return acc; | ||
| acc[t] = Object.prototype.hasOwnProperty.call(acc, t) ? acc[t] + 1 : 1; |
| // 计算标签频次并按热度排序 | ||
| const tagCounts = posts.flatMap((p) => p.tags).reduce<Record<string, number>>((acc, t) => { | ||
| if (t === "__proto__" || t === "constructor") return acc; | ||
| acc[t] = Object.prototype.hasOwnProperty.call(acc, t) ? acc[t] + 1 : 1; |
|
主人,您的更新非常棒!所有的代码检查、安全检查、CodeQL 分析都已 100% 跑通,没有发现任何异常。\n\n已经确认各项安全改造和超时控制策略配置正确!庄园的代码依然洁净如初。✅\n\n*(注:鉴于女仆与您共享同样的身份验证,无法以 Approve 的形式通过审核。由于检查已全部绿灯,您可以随时下令让我为您 Merge,或者您亲自进行 Merge!)* |
变更描述
1. 🏠 Monolith MCP 服务器
为博客打造的全权管理 MCP 服务器,共 30 个工具:
2. 🐛 修复 marked v15 Markdown 渲染兼容性问题
marked v15 传入 renderer 的参数结构发生了变化:
text字段变成了原始 Markdown 文本,header/rows变成了 Token 数组。旧代码直接拼接导致渲染异常。修复的 3 个 renderer:
table[object Object]this.parser.parseInline()递归解析 cell tokensheading**粗体**\代码`` 原样显示function,用 parseInline 生成 HTMLlink根因:
new marked.Renderer()+ 箭头函数的this不绑定 parser,无法调用this.parser.parseInline()。改用function声明后 marked 在调用时自动绑定 parser 上下文。变更类型
测试
this.parser.parseInline()正确输出 HTML