Skip to content

feat: 独立页动态导航、后台侧栏固定、移动端代码块优化、标签折叠#40

Merged
one-ea merged 6 commits into
mainfrom
dev
Apr 21, 2026
Merged

feat: 独立页动态导航、后台侧栏固定、移动端代码块优化、标签折叠#40
one-ea merged 6 commits into
mainfrom
dev

Conversation

@one-ea
Copy link
Copy Markdown
Owner

@one-ea one-ea commented Apr 21, 2026

Summary

  • 独立页动态导航:navbar 和 footer 从 /api/pages 获取 showInNav=true 的页面动态注入导航栏,归档和关于始终在最后
  • 后台侧栏固定:admin-layout 侧栏改用 fixed 定位,彻底脱离文档流,内容区独立滚动
  • 移动端代码块优化:代码行改用 flex 布局,行号 sticky left-0、顶栏 sticky left-0,尺寸压缩释放内容空间
  • 标签折叠展示:后台仪表盘标签区默认折叠 2 行,超过 8 个显示展开按钮
  • 清理:删除 .kilo、.playwright-mcp 残留,重写 .gitignore
  • 新增:AGENTS.md 项目规则、opencode.json 项目配置(MCP + 记忆库引用)

one-ea added 6 commits April 19, 2026 08:25
Critical/High:
- Sanitize custom_header/footer through DOMPurify before injection,
  forbid inline scripts, only allow external script src (fixes #1)
- Add security headers: X-Content-Type-Options, X-Frame-Options,
  Referrer-Policy, Permissions-Policy, HSTS (fixes #5)
- Remove authorEmail from public comment API, use DiceBear avatars
  based on nickname instead of Gravatar hash (fixes #2, #14)
- Add login rate limiting: 5 attempts per 15 min per IP (fixes #3)
- Reflect request Origin in CORS instead of wildcard (fixes #4)
- Add SSRF protection for WebDAV backup and image localization:
  only allow https://, block private/internal IPs (fixes #6, #13)
- Filter javascript: URIs in markdown link renderer (fixes #7)

Medium:
- Replace hardcoded reaction salt with REACTION_SALT env var (fixes #9)
- Add .env.* to .gitignore, remove .env.production from tracking (fixes #10)
- Disable source maps in production build (fixes #11)
- Remove infrastructure details from health endpoint (fixes #15)
…PR notice

- Add CookieConsent component: bottom banner with accept/reject,
  stores consent in localStorage, fires 'cookie-consent-accepted'
  event for third-party script injection gating
- Add /privacy route with full privacy policy page (bilingual)
- Gate custom_header/footer script injection behind cookie consent:
  scripts only load after user accepts; non-script HTML injects
  immediately as before
- Add GDPR warning in admin settings 'Extensions & Injection' tab
- Add PRIVACY.md to repo root
- Add privacy page link in cookie consent banner
- code-block-wrapper 增加 -webkit-overflow-scrolling: touch(iOS 惯性滚动)
- pre 和 code 元素增加 white-space: pre 强制不换行,内容真实撑开 wrapper
- 移动端 pre 增加 width: max-content + min-width: 100% 确保可超出容器宽度
- code-line 从 display: inline-block 改为 display: block,避免 min-width: 100%
  在移动端按父容器宽度错误计算导致代码不撑开横向滚动区域
- 兜底 pre:not(.hljs) 同步修复
code-line 改回 display: inline-block,移除 width: max-content 和
min-width: calc(100%+8px) 的循环依赖写法(该写法导致移动端代码
内容完全消失)。pre 的 white-space: pre 已足够阻止换行,
wrapper 的 overflow-x: auto 负责横向滚动。
- navbar: 从 /api/pages 获取 showInNav=true 的页面动态注入导航栏
- footer: 底部新增独立页面链接
- admin-layout: 侧栏改用 fixed 定位,彻底脱离文档流
- dashboard: 标签区折叠展开,默认显示2行+展开按钮
- globals.css: 移动端代码块优化(行号flex布局、顶栏sticky、尺寸缩减)
- .gitignore: 重写并清理不存在的条目
- 清理 .kilo 和 .playwright-mcp 残留
- 新增 AGENTS.md 和 opencode.json 项目配置
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Summary by CodeRabbit

发布说明

  • 新功能

    • 页面导航现支持动态链接显示,页脚和导航栏可展示自定义页面
  • 样式

    • 改进代码块的水平滚动交互和移动端视觉表现
    • 优化管理后台侧边栏布局和响应式设计
  • 文档

    • 新增项目规则和开发指南文档

Walkthrough

本次更新通过引入动态导航页面获取能力、重构前端组件结构和样式,以及补充项目文档和开发配置来增强应用功能。新增API接口支持按需加载展示在导航栏中的页面,同时优化了admin布局、代码块样式和移动端响应式表现。

Changes

Cohort / File(s) Summary
配置和文档更新
.gitignore, AGENTS.md, opencode.json
.gitignore重新组织并更加明确的敏感文件排除规则(环境变量、日志、编辑器临时文件);新增AGENTS.md记录项目架构和开发规范;opencode.json配置MCP集成(GitHub、Playwright、Upstash)
动态导航页面API
client/src/lib/api.ts
新增NavPage类型定义和fetchNavPages()异步函数,支持按需获取启用导航显示的页面列表,采用60秒缓存策略
导航栏组件
client/src/components/navbar.tsx, client/src/components/footer.tsx
两个组件均新增调用fetchNavPages()动态获取页面导航数据;Navbar使用useMemo构建组合导航链接;Footer条件渲染导航页面列表
Admin布局和样式
client/src/components/admin-layout.tsx
侧边栏从sticky改为fixed定位实现固定左侧布局;重构footer为独立子组件;优化移动端drawer语义(添加role="dialog"aria-label);调整响应式间距和图标大小
全局代码块样式优化
client/src/globals.css
改进.prose-monolith代码块横向滚动体验:添加iOS动量滚动、sticky定位保持header可见、调整行号布局为flex;优化移动端(≤768px)的padding和边框radius
Admin仪表盘
client/src/pages/admin/dashboard.tsx
右侧边栏改为lg:sticky定位;标签列表添加展开/折叠切换(超过8个标签时出现),折叠时限制高度为max-h-[64px]
Playwright快照文件
.playwright-mcp/page-*.yml
删除两个过期的Playwright MCP页面快照定义

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser<br/>(Client)
    participant Navbar as Navbar<br/>Component
    participant Footer as Footer<br/>Component
    participant API as API<br/>(/api/pages)
    participant Cache as Cache<br/>(60s TTL)

    Browser->>Navbar: Mount Navbar component
    Navbar->>API: fetchNavPages()
    API->>Cache: Check cache
    alt Cache Hit
        Cache-->>API: Return cached data
    else Cache Miss
        API-->>API: Fetch /api/pages
        API->>Cache: Store with 60s TTL
    end
    API-->>Navbar: NavPage[] (filtered by showInNav)
    Navbar->>Navbar: useMemo: Build nav links<br/>(/, /page/*, /archive, /about)
    Navbar-->>Browser: Render navigation

    Browser->>Footer: Mount Footer component
    Footer->>API: fetchNavPages()
    API->>Cache: Check cache
    Cache-->>API: Return cached data
    API-->>Footer: NavPage[]
    Footer->>Footer: Conditional render<br/>nav section
    Footer-->>Browser: Render nav pages + footer text
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

frontend, feature, docs, styles

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题遵循 Conventional Commits 格式 feat(scope): description,但缺少 scope。标题内容准确反映了 PR 的主要改动:动态导航、侧栏固定、代码块优化和标签折叠。
Description check ✅ Passed 描述详细列举了 PR 的五大主要变化及清理工作,与变更集中的各文件改动完全对应,信息充分且清晰。
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch dev

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.

❤️ Share

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

@coderabbitai coderabbitai Bot added frontend 前端 (React/Vite) 相关变更 styles CSS/UI 样式调整 feature 新功能 docs 文档更新 labels Apr 21, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (1)
client/src/components/admin-layout.tsx (1)

65-135: 🛠️ Refactor suggestion | 🟠 Major

子组件定义在父组件内部会导致每次渲染重新挂载

SidebarFooterSidebarContent 定义在 AdminLayout 内部,每次 AdminLayout 渲染(如 mobileMenuOpenlocation 变化)时它们都是全新的组件类型,React 会卸载旧树并重新挂载内部所有子节点(LinkThemeToggle 等均会丢失内部状态/动画)。建议提升为模块级组件,或改为普通渲染函数(renderSidebar())直接返回 JSX,避免组件身份每次变化。

建议改动
-  const SidebarFooter = () => (
+  const renderSidebarFooter = () => (
     <div className="border-t border-border/40 p-[12px] space-y-[2px]">
       ...
     </div>
   );

-  const SidebarContent = () => (
+  const renderSidebarContent = () => (
     <div className="flex flex-col h-full">
       ...
-      <SidebarFooter />
+      {renderSidebarFooter()}
     </div>
   );

并在使用处改为 {renderSidebarContent()}

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/components/admin-layout.tsx` around lines 65 - 135, SidebarFooter
and SidebarContent are declared inside AdminLayout which causes them to be
recreated on every render and forces React to remount their subtrees; move
SidebarFooter and SidebarContent out of the AdminLayout function to module-level
components (or convert them to pure render functions like
renderSidebarContent/renderSidebarFooter that return JSX) and update AdminLayout
to use the top-level SidebarFooter and SidebarContent (or call the
renderSidebar... functions) so their identity is stable across renders and child
state/animations are preserved.
🧹 Nitpick comments (3)
client/src/components/footer.tsx (1)

18-20: fetchNavPages 的 Promise rejection 未处理

虽然 fetchNavPages 内部已 try/catch 并返回 [],但如果未来实现变化,这里没有 .catch 会抛未处理拒绝。建议与上面 fetch 一致显式补一个 .catch(() => {}) 防御。此外,navbar 与 footer 都在挂载时独立调用 fetchNavPages,得益于 fetchJsonWithCache 的 in-flight 去重,实际只发一次请求——改动合理。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/components/footer.tsx` around lines 18 - 20, The useEffect in
footer calls fetchNavPages().then(setNavPages) without handling Promise
rejections; update the effect to mirror navbar by appending a .catch(() => {})
to the fetchNavPages() promise chain so any rejection is swallowed and cannot
become an unhandled rejection (keep setNavPages in the then branch); this change
touches the useEffect that calls fetchNavPages and aligns with the in-flight
dedupe behavior of fetchJsonWithCache.
client/src/lib/api.ts (1)

123-130: 建议:服务端直接过滤 + 排序,避免客户端重复处理

目前 /api/pages 返回所有已发布页面(含 showInNav=false),客户端再过滤。这会把不需要的页面数据打包进缓存并暴露到公共 API。建议在 getPublishedPages() 中按 showInNav=true 过滤,或新增专用的 /api/pages/nav 端点。另外,此处依赖服务端按 sortOrder 升序返回,但客户端类型已暴露 sortOrder 字段却未做兜底排序,建议 .sort((a,b)=>a.sortOrder-b.sortOrder) 以防后端实现变更。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/lib/api.ts` around lines 123 - 130, fetchNavPages currently
fetches all pages from "/api/pages" and filters client-side; change the server
to return only nav pages (filter showInNav=true in getPublishedPages or add a
new endpoint /api/pages/nav) so fetchNavPages calls that endpoint (via
fetchJsonWithCache) to avoid caching/exposing non-nav pages, and also add a
defensive client-side sort fallback in fetchNavPages by sorting the returned
NavPage[] by sortOrder (e.g., .sort((a,b)=>a.sortOrder - b.sortOrder)) to guard
against backend order changes.
opencode.json (1)

14-14: 固定 @playwright/mcp 版本,避免使用 @latest

使用 @latest 会导致行为随时间漂移。官方发布历史显示存在多个破坏性变化:--no-sandbox 标志重命名、--isolated--in-memory 参数变更、移除 --shared-browser-context 选项等。建议固定至当前稳定版本(如 @playwright/mcp@0.0.70)以确保可复现性和可审计性。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode.json` at line 14, The command entry currently uses the unstable
floating tag "@playwright/mcp@latest"; change the package spec in the "command"
array to a pinned, stable version (e.g., "@playwright/mcp@0.0.70") to prevent
behavioral drift—update the JSON "command" value where the package string
appears so the CI/local runs use a fixed mcp version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AGENTS.md`:
- Line 7: 行内 Markdown 对 `/api/* /cdn/* /rss.xml` 中的星号触发 MD037 报警;在 AGENTS.md
中定位包含文本 "代理层:Pages Functions(反向代理 /api/* /cdn/* /rss.xml)"
的行,然后将每个路径或整个路径序列包裹成代码样式(例如用反引号)或对星号进行转义,以消除 MD037 lint 警告并保持可读性。
- Around line 61-63: Remove the sensitive entries in AGENTS.md: delete the
explicit "GitHub PAT:已配置在 system_config.md" line and the plaintext backend
password "monolith2026", and replace them with a short instruction stating that
credentials must never be stored in repo docs and should be injected at runtime
via the organization's secret management solution (e.g., "Use secret manager /
environment-injected credentials only"). Ensure any mention of branch protection
stays but does not reference credential locations; also run a repo-wide search
for the strings "GitHub PAT" and "monolith2026" and remove or rotate any other
occurrences.
- Line 47: AGENTS.md 中第 47 行关于 Hono v4 的 verify() 用法不准确;更新该行将声明从“verify() 需要 3
个参数:verify(token, secret, "HS256")”改为说明 verify(token: string, secret: string,
alg?: 'HS256'),即算法参数为可选且默认 HS256,因此可以直接调用 verify(token, secret),并在同处添加对 Hono
当前锁定版本官方文档链接 https://hono.dev/docs/helpers/jwt 作为参考以防版本变化。

In `@client/src/components/admin-layout.tsx`:
- Around line 149-184: The mobile menu button uses
aria-controls="admin-mobile-navigation" but no element has that id; update the
mobile drawer (the <aside> that renders SidebarContent in admin-layout.tsx) to
include id="admin-mobile-navigation" (or add the id to the internal <nav> inside
SidebarContent) and also add aria-modal="true" alongside role="dialog" to
reflect modal semantics; ensure the id you add matches the aria-controls on the
button and keep the existing setMobileMenuOpen state logic unchanged.

In `@client/src/components/navbar.tsx`:
- Around line 28-35: navLinks builds hrefs by concatenating p.slug into
`/page/${p.slug}` which breaks for spaces/Chinese/special characters; update the
construction inside the useMemo (and the analogous footer code) to URL-encode
the slug using encodeURIComponent(p.slug) so generated hrefs are valid and
routes resolve correctly, and keep the dependency array unchanged (still depends
on navPages).

In `@client/src/globals.css`:
- Around line 898-902: The mobile-only border-radius override is currently
scoped to :root[data-theme="light"] .prose-monolith .code-block-wrapper which
leaves the dark/default theme unaffected; change the media-query rule so it
applies regardless of theme by moving/removing the [data-theme="light"]
qualifier (i.e., use `@media` (max-width: 768px) { .prose-monolith
.code-block-wrapper { border-radius: 0; } }) or add an equivalent rule for the
dark/default theme to ensure both themes get border-radius: 0 on mobile.
- Around line 699-712: The highlighted backgrounds are getting clipped when
lines scroll horizontally because .code-line is constrained to the container
width while .line-content can shrink; update .code-line (not .line-content) to
allow its box to grow with content and still fill short lines by setting it to
an inline-flex layout with width: max-content and min-width: 100% (keep existing
padding/margins/border-radius), so long lines expand the background past the
viewport while short lines still occupy the full container width; reference
.code-line and .line-content and ensure .line-content retains white-space: pre
and min-width: 0.

In `@client/src/pages/admin/dashboard.tsx`:
- Around line 340-348: The collapse behavior is inconsistent: the UI decides
collapse by allTags.length > 8 but actually clips by CSS max-h-[64px], which can
silently hide tags when labels wrap; change the collapsed rendering in the tags
block (where allTags, tagExpanded and setTagExpanded are used) to explicitly
render only the first N items when !tagExpanded (e.g. render allTags.slice(0, 8)
and keep the "+N" button logic) and remove the pixel height clip (drop
max-h-[64px] overflow-hidden) or replace it with a proper line-clamp approach if
you prefer; additionally, update the right column that uses lg:sticky
lg:top-[24px] to include a height constraint and scrolling (e.g. add
max-h-[calc(100vh-48px)] overflow-y-auto) so the sticky sidebar can scroll when
its content exceeds the viewport.

In `@opencode.json`:
- Around line 23-33: The opencode.json "instructions" array references files in
the ignored memory_bank directory (entries under the memory_bank folder), which
will be missing on clean clones/CI; update the "instructions" array to point to
a tracked location or to a public read-only mirror (or add a checked-in copy) so
CI and new dev machines can access them reliably, and ensure any loader falls
back to an alternate source (env var or URL) if those entries are unavailable.

---

Outside diff comments:
In `@client/src/components/admin-layout.tsx`:
- Around line 65-135: SidebarFooter and SidebarContent are declared inside
AdminLayout which causes them to be recreated on every render and forces React
to remount their subtrees; move SidebarFooter and SidebarContent out of the
AdminLayout function to module-level components (or convert them to pure render
functions like renderSidebarContent/renderSidebarFooter that return JSX) and
update AdminLayout to use the top-level SidebarFooter and SidebarContent (or
call the renderSidebar... functions) so their identity is stable across renders
and child state/animations are preserved.

---

Nitpick comments:
In `@client/src/components/footer.tsx`:
- Around line 18-20: The useEffect in footer calls
fetchNavPages().then(setNavPages) without handling Promise rejections; update
the effect to mirror navbar by appending a .catch(() => {}) to the
fetchNavPages() promise chain so any rejection is swallowed and cannot become an
unhandled rejection (keep setNavPages in the then branch); this change touches
the useEffect that calls fetchNavPages and aligns with the in-flight dedupe
behavior of fetchJsonWithCache.

In `@client/src/lib/api.ts`:
- Around line 123-130: fetchNavPages currently fetches all pages from
"/api/pages" and filters client-side; change the server to return only nav pages
(filter showInNav=true in getPublishedPages or add a new endpoint
/api/pages/nav) so fetchNavPages calls that endpoint (via fetchJsonWithCache) to
avoid caching/exposing non-nav pages, and also add a defensive client-side sort
fallback in fetchNavPages by sorting the returned NavPage[] by sortOrder (e.g.,
.sort((a,b)=>a.sortOrder - b.sortOrder)) to guard against backend order changes.

In `@opencode.json`:
- Line 14: The command entry currently uses the unstable floating tag
"@playwright/mcp@latest"; change the package spec in the "command" array to a
pinned, stable version (e.g., "@playwright/mcp@0.0.70") to prevent behavioral
drift—update the JSON "command" value where the package string appears so the
CI/local runs use a fixed mcp version.
🪄 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: 6fe863e3-15ec-4d98-9a8e-bfa07bc09e50

📥 Commits

Reviewing files that changed from the base of the PR and between 3aaa008 and da2a3ca.

⛔ Files ignored due to path filters (3)
  • .kilo/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • .playwright-mcp/console-2026-04-19T04-35-34-885Z.log is excluded by !**/*.log
  • .playwright-mcp/page-2026-04-19T04-35-58-544Z.png is excluded by !**/*.png
📒 Files selected for processing (11)
  • .gitignore
  • .playwright-mcp/page-2026-04-19T04-35-40-096Z.yml
  • .playwright-mcp/page-2026-04-19T04-35-49-532Z.yml
  • AGENTS.md
  • client/src/components/admin-layout.tsx
  • client/src/components/footer.tsx
  • client/src/components/navbar.tsx
  • client/src/globals.css
  • client/src/lib/api.ts
  • client/src/pages/admin/dashboard.tsx
  • opencode.json
💤 Files with no reviewable changes (2)
  • .playwright-mcp/page-2026-04-19T04-35-40-096Z.yml
  • .playwright-mcp/page-2026-04-19T04-35-49-532Z.yml
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use DOMPurify to sanitize Markdown rendered output with whitelist allowing iframe/video embedding

Files:

  • client/src/pages/admin/dashboard.tsx
  • client/src/components/navbar.tsx
  • client/src/components/footer.tsx
  • client/src/lib/api.ts
  • client/src/components/admin-layout.tsx
client/src/pages/**

⚙️ CodeRabbit configuration file

client/src/pages/**: 页面级组件。审查时请关注: 1. 数据加载和错误处理是否完善 2. SEO 相关(页面标题、meta 标签) 3. 导航和路由是否正确

Files:

  • client/src/pages/admin/dashboard.tsx
client/src/components/**

⚙️ CodeRabbit configuration file

client/src/components/**: 这是 React 前端组件目录。审查时请关注: 1. 是否同时兼容暗色和亮色主题(检查 CSS 变量和 data-theme) 2. 响应式布局是否完整(移动端/平板/桌面端) 3. 无障碍访问(aria 标签、键盘导航) 4. 组件是否保持单一职责

Files:

  • client/src/components/navbar.tsx
  • client/src/components/footer.tsx
  • client/src/components/admin-layout.tsx
.gitignore

📄 CodeRabbit inference engine (AGENTS.md)

package-lock.json must never be added to .gitignore

Files:

  • .gitignore
client/src/globals.css

⚙️ CodeRabbit configuration file

client/src/globals.css: 全局样式和 CSS 变量系统。审查时请关注: 1. [data-theme="light"] 和默认暗色主题的变量是否配对 2. OKLCH 色值的明度/色度是否合理 3. 是否有遗漏的选择器未覆盖亮色模式

Files:

  • client/src/globals.css
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Use adapter pattern for storage layer: routes should only depend on `IDatabase` / `IObjectStorage` interfaces, with provider switching via `DB_PROVIDER` / `STORAGE_PROVIDER` environment variables
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Use `npm run deploy:cloudflare` for deployments; never manually execute `wrangler pages deploy`
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Always deploy from `client/` directory to ensure `dist` includes Pages Functions, never from other directories
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Use dev branch for feature development; follow dev → commit → push → PR → squash merge to main workflow; never push directly to main branch
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Frontend uses Vite 6, React 19, Tailwind CSS v4, shadcn/ui, and wouter for routing
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Backend uses Hono Workers with Drizzle ORM for data access
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: Storage system supports D1 (SQLite), R2 (object storage), with ability to switch to Turso/PostgreSQL/S3
Learnt from: CR
Repo: one-ea/Monolith

Timestamp: 2026-04-21T14:25:25.791Z
Learning: CI must inject ADMIN_PASSWORD and JWT_SECRET via `wrangler secret put` in the Sync Worker secrets step
🪛 LanguageTool
AGENTS.md

[grammar] ~41-~41: Ensure spelling is correct
Context: ....gitignore - CI "Sync Worker secrets" 步骤通过 wrangler secret put 注入 ADMIN_PASSWORD...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.0)
AGENTS.md

[warning] 7-7: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)

🔇 Additional comments (3)
.gitignore (1)

46-49: package-lock.json 跟踪策略正确,符合仓库规范。

这里明确声明了 package-lock.json 必须纳入版本管理,和当前 .gitignore 规则保持一致。

As per coding guidelines .gitignore: package-lock.json must never be added to .gitignore.

client/src/globals.css (2)

410-427: LGTM:横向滚动职责划分更清晰。

由 wrapper/独立 pre 负责横向滚动,pre 保持 white-space: pre,并补上 iOS 惯性滚动,整体对移动端代码块更稳。

Also applies to: 477-486


578-592: LGTM:横向滚动时的固定元素处理到位。

code-headercode-title-bar 和行号都固定在 left: 0,并设置了层级和背景,能减少移动端横向滚动时的 UI 丢失感。

Also applies to: 653-667, 715-729

Comment thread AGENTS.md
Monolith 是基于 Cloudflare 边缘计算的全栈博客系统。
- 前端:Vite 6 + React 19 + Tailwind CSS v4 + shadcn/ui + wouter
- 后端:Hono Workers + Drizzle ORM
- 代理层:Pages Functions(反向代理 /api/* /cdn/* /rss.xml)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

修复 Markdown 语法以避免 lint 告警。

/api/* /cdn/* /rss.xml 这类写法容易触发 MD037,建议对 * 转义或整体包裹代码样式,避免 CI 文档检查噪音。

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 7-7: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` at line 7, 行内 Markdown 对 `/api/* /cdn/* /rss.xml` 中的星号触发 MD037
报警;在 AGENTS.md 中定位包含文本 "代理层:Pages Functions(反向代理 /api/* /cdn/* /rss.xml)"
的行,然后将每个路径或整个路径序列包裹成代码样式(例如用反引号)或对星号进行转义,以消除 MD037 lint 警告并保持可读性。

Comment thread AGENTS.md
- D1 迁移标记不同步时需手动 `INSERT INTO d1_migrations`
- Cloudflare CDN 有短暂缓存,验证时用部署唯一 URL
- 本地 `client/functions/api/` 目录的 tsconfig 独立,不与主项目混用
- Hono v4 `verify()` 需要 3 个参数:`verify(token, secret, "HS256")`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Hono v4, what is the exact function signature of jwt verify(), and does it require an explicit algorithm argument like "HS256"?

💡 Result:

In Hono v4 (hono/jwt), the jwt verify helper function signature is: verify(token: string, secret: string, alg?: 'HS256'; issuer?: string | RegExp): Promise It does NOT require an explicit algorithm argument like "HS256". Reason: the docs show the alg parameter as optional (alg?: ... ) and note that the default is HS256. In other words, you can call verify(token, secret) and Hono will use the default algorithm.

Citations:


更正 Hono v4 jwt verify() 参数用法。

AGENTS.md 第 47 行声称 verify() 需要 3 个参数,但根据 Hono v4 官方文档,算法参数是可选的。正确签名为 verify(token: string, secret: string, alg?: 'HS256'),默认采用 HS256,可直接调用 verify(token, secret) 而无需传入第三参数。建议更新文档说明,并引用 Hono 当前锁定版本的官方文档链接(https://hono.dev/docs/helpers/jwt)以避免版本更新时的信息偏差。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` at line 47, AGENTS.md 中第 47 行关于 Hono v4 的 verify()
用法不准确;更新该行将声明从“verify() 需要 3 个参数:verify(token, secret, "HS256")”改为说明
verify(token: string, secret: string, alg?: 'HS256'),即算法参数为可选且默认 HS256,因此可以直接调用
verify(token, secret),并在同处添加对 Hono 当前锁定版本官方文档链接
https://hono.dev/docs/helpers/jwt 作为参考以防版本变化。

Comment thread AGENTS.md
Comment on lines +61 to +63
- GitHub PAT:已配置在 system_config.md
- 后台密码:`monolith2026`
- 分支保护:main 需要 1 人审批 + 线性历史 + 状态检查 No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

文档中出现敏感信息与明文密码,需立即移除。

GitHub PAT 存放位置说明 + 明文后台密码会直接扩大泄露面。请改为“仅通过密钥管理系统注入”,不要在仓库文档中出现任何可用凭据或固定口令。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 61 - 63, Remove the sensitive entries in AGENTS.md:
delete the explicit "GitHub PAT:已配置在 system_config.md" line and the plaintext
backend password "monolith2026", and replace them with a short instruction
stating that credentials must never be stored in repo docs and should be
injected at runtime via the organization's secret management solution (e.g.,
"Use secret manager / environment-injected credentials only"). Ensure any
mention of branch protection stays but does not reference credential locations;
also run a repo-wide search for the strings "GitHub PAT" and "monolith2026" and
remove or rotate any other occurrences.

Comment on lines +149 to 184
<aside
role="dialog"
aria-label="导航菜单"
className="relative flex flex-col w-[260px] max-w-[80vw] h-full bg-background shadow-2xl animate-in slide-in-from-left"
>
<SidebarContent />
</aside>
</div>
)}

{/* Main Content */}
<main className="flex-1 flex flex-col min-w-0">
{/* Mobile Header */}
<header className="md:hidden sticky top-0 z-40 flex items-center justify-between px-4 h-14 border-b bg-background/80 backdrop-blur-md">
<div className="flex items-center gap-2 font-semibold">
<div className="w-6 h-6 rounded bg-foreground text-background flex items-center justify-center text-xs font-bold">M</div>
<span>Admin</span>
<main className="md:ml-[240px] min-h-screen overflow-y-auto">
<header className="md:hidden sticky top-0 z-40 flex items-center justify-between px-[16px] h-[52px] border-b border-border/40 bg-background/80 backdrop-blur-md shrink-0">
<div className="flex items-center gap-[8px] font-semibold text-[14px]">
<div className="w-[24px] h-[24px] rounded bg-foreground text-background flex items-center justify-center text-[11px] font-bold">M</div>
<span>Admin</span>
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-[4px]">
<ThemeToggle />
<a
href="/"
target="_blank"
rel="noopener noreferrer"
className="p-[6px] text-muted-foreground/50 hover:text-foreground transition-colors"
aria-label="查看站点"
>
<ExternalLink className="w-[16px] h-[16px]" />
</a>
<button
onClick={() => setMobileMenuOpen(true)}
className="p-2 -mr-2 text-muted-foreground hover:text-foreground"
aria-label="打开后台导航菜单"
aria-expanded={mobileMenuOpen}
className="p-[6px] text-muted-foreground/50 hover:text-foreground transition-colors"
aria-label="打开导航菜单"
aria-expanded={mobileMenuOpen}
aria-controls="admin-mobile-navigation"
>
<Menu className="w-5 h-5" />
<Menu className="w-[18px] h-[18px]" />
</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

aria-controls 指向的元素不存在

移动端菜单按钮声明了 aria-controls="admin-mobile-navigation",但 aside / nav 上都没有 id="admin-mobile-navigation",屏幕阅读器无法定位被控元素。请给 mobile 抽屉的 <aside> 或其中的 <nav> 加上对应 id。顺便建议 role="dialog" 配合 aria-modal="true" 使用,以匹配真正的模态语义。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/components/admin-layout.tsx` around lines 149 - 184, The mobile
menu button uses aria-controls="admin-mobile-navigation" but no element has that
id; update the mobile drawer (the <aside> that renders SidebarContent in
admin-layout.tsx) to include id="admin-mobile-navigation" (or add the id to the
internal <nav> inside SidebarContent) and also add aria-modal="true" alongside
role="dialog" to reflect modal semantics; ensure the id you add matches the
aria-controls on the button and keep the existing setMobileMenuOpen state logic
unchanged.

Comment on lines +28 to +35
const navLinks = useMemo(
() => [
...fixedStart,
...navPages.map((p) => ({ href: `/page/${p.slug}`, label: p.title })),
...fixedEnd,
],
[navPages],
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

slug 未进行 URL 编码

`/page/${p.slug}` 直接拼接 slug,若后端未强校验 slug 字符集(服务端 /api/pages 未对 slug 做格式校验),含中文/空格/特殊字符的 slug 会生成无法正确路由的 href。建议 encodeURIComponent(p.slug),footer 同位置也建议同步处理。

-      ...navPages.map((p) => ({ href: `/page/${p.slug}`, label: p.title })),
+      ...navPages.map((p) => ({ href: `/page/${encodeURIComponent(p.slug)}`, label: p.title })),
📝 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.

Suggested change
const navLinks = useMemo(
() => [
...fixedStart,
...navPages.map((p) => ({ href: `/page/${p.slug}`, label: p.title })),
...fixedEnd,
],
[navPages],
);
const navLinks = useMemo(
() => [
...fixedStart,
...navPages.map((p) => ({ href: `/page/${encodeURIComponent(p.slug)}`, label: p.title })),
...fixedEnd,
],
[navPages],
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/components/navbar.tsx` around lines 28 - 35, navLinks builds hrefs
by concatenating p.slug into `/page/${p.slug}` which breaks for
spaces/Chinese/special characters; update the construction inside the useMemo
(and the analogous footer code) to URL-encode the slug using
encodeURIComponent(p.slug) so generated hrefs are valid and routes resolve
correctly, and keep the dependency array unchanged (still depends on navPages).

Comment thread client/src/globals.css
Comment on lines 699 to +712
/* 代码行布局 */
.prose-monolith pre code .code-line {
display: inline-block;
min-width: 100%;
display: flex;
align-items: baseline;
padding: 0 4px;
margin: 0 -4px;
border-radius: 2px;
}

.prose-monolith pre code .line-content {
flex: 1;
min-width: 0;
white-space: pre;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

避免长行高亮背景在横向滚动后被截断。

line-content 允许收缩到容器宽度后,长代码只是在视觉上溢出,.code-line 自身背景宽度可能仍停留在视口宽度;这会影响后面的 line-highlight / diff-add / diff-del 长行展示。建议让行盒按内容撑开,同时保留短行至少占满容器宽度。

🎨 建议调整
 .prose-monolith pre code .code-line {
   display: flex;
   align-items: baseline;
+  width: max-content;
+  min-width: 100%;
   padding: 0 4px;
   margin: 0 -4px;
   border-radius: 2px;
 }

 .prose-monolith pre code .line-content {
-  flex: 1;
-  min-width: 0;
+  flex: 0 0 auto;
+  min-width: max-content;
   white-space: pre;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/globals.css` around lines 699 - 712, The highlighted backgrounds
are getting clipped when lines scroll horizontally because .code-line is
constrained to the container width while .line-content can shrink; update
.code-line (not .line-content) to allow its box to grow with content and still
fill short lines by setting it to an inline-flex layout with width: max-content
and min-width: 100% (keep existing padding/margins/border-radius), so long lines
expand the background past the viewport while short lines still occupy the full
container width; reference .code-line and .line-content and ensure .line-content
retains white-space: pre and min-width: 0.

Comment thread client/src/globals.css
Comment on lines +898 to +902
@media (max-width: 768px) {
:root[data-theme="light"] .prose-monolith .code-block-wrapper {
border-radius: 0;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

移动端圆角覆盖应同时适用于暗色主题。

这里把 border-radius: 0 限定在 light theme,默认暗色主题移动端仍保留 8px,会造成双主题响应式表现不一致。建议改成主题无关规则。

🎨 建议调整
 `@media` (max-width: 768px) {
-  :root[data-theme="light"] .prose-monolith .code-block-wrapper {
+  .prose-monolith .code-block-wrapper {
     border-radius: 0;
   }
 }

As per coding guidelines, client/src/globals.css: 审查时请关注:1. [data-theme="light"] 和默认暗色主题的变量是否配对 3. 是否有遗漏的选择器未覆盖亮色模式.

📝 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.

Suggested change
@media (max-width: 768px) {
:root[data-theme="light"] .prose-monolith .code-block-wrapper {
border-radius: 0;
}
}
`@media` (max-width: 768px) {
.prose-monolith .code-block-wrapper {
border-radius: 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 898 - 902, The mobile-only border-radius
override is currently scoped to :root[data-theme="light"] .prose-monolith
.code-block-wrapper which leaves the dark/default theme unaffected; change the
media-query rule so it applies regardless of theme by moving/removing the
[data-theme="light"] qualifier (i.e., use `@media` (max-width: 768px) {
.prose-monolith .code-block-wrapper { border-radius: 0; } }) or add an
equivalent rule for the dark/default theme to ensure both themes get
border-radius: 0 on mobile.

Comment on lines +340 to +348
<div className="flex items-center justify-between mb-[8px]">
<h3 className="text-[10px] font-medium text-muted-foreground/30 uppercase tracking-wider">标签</h3>
{allTags.length > 8 && (
<button onClick={() => setTagExpanded(!tagExpanded)} className="text-[10px] text-cyan-400/60 hover:text-cyan-400 transition-colors">
{tagExpanded ? "收起" : `+${allTags.length - 8}`}
</button>
)}
</div>
<div className={`flex flex-wrap gap-[4px] ${!tagExpanded ? "max-h-[64px] overflow-hidden" : ""}`}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

折叠阈值与高度裁剪不一致

展开按钮判定是 allTags.length > 8,但折叠态使用 max-h-[64px](约 2 行)做像素裁剪。当标签名较长、8 个标签已经换成 3 行,或者标签数 ≤ 8 但单个较长导致超过 2 行时,标签会被静默裁掉且没有展开入口。建议统一以行数/数量为准,例如使用 line-clamp 思路或只显示前 N 个再以 +N 展示其余数量。

同时,两栏布局 lg:grid-cols-[1fr_220px] 下右侧栏用 lg:sticky lg:top-[24px],若侧栏高度超过视口会出现无法滚动到底部的问题,建议加 max-h-[calc(100vh-48px)] overflow-y-auto 以兼顾长内容场景。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/pages/admin/dashboard.tsx` around lines 340 - 348, The collapse
behavior is inconsistent: the UI decides collapse by allTags.length > 8 but
actually clips by CSS max-h-[64px], which can silently hide tags when labels
wrap; change the collapsed rendering in the tags block (where allTags,
tagExpanded and setTagExpanded are used) to explicitly render only the first N
items when !tagExpanded (e.g. render allTags.slice(0, 8) and keep the "+N"
button logic) and remove the pixel height clip (drop max-h-[64px]
overflow-hidden) or replace it with a proper line-clamp approach if you prefer;
additionally, update the right column that uses lg:sticky lg:top-[24px] to
include a height constraint and scrolling (e.g. add max-h-[calc(100vh-48px)]
overflow-y-auto) so the sticky sidebar can scroll when its content exceeds the
viewport.

Comment thread opencode.json
Comment on lines +23 to +33
"instructions": [
".agents/memory_bank/README.md",
".agents/memory_bank/master_preferences.md",
".agents/memory_bank/monolith_architecture.md",
".agents/memory_bank/monolith_roadmap.md",
".agents/memory_bank/monolith_v1_status.md",
".agents/memory_bank/monolith_security_hardening.md",
".agents/memory_bank/system_config.md",
".agents/memory_bank/ui_design_parameters.md",
".agents/memory_bank/eslint_security_guide.md",
".agents/memory_bank/typecho_dev_guide.md"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

instructions 指向被忽略目录,干净环境下会失效。

当前配置依赖 .agents/memory_bank/*,但该目录被 .gitignore 忽略,新的开发机或 CI 无法稳定拿到这些文件。建议改为可追踪路径,或提供可公开同步的只读副本目录。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode.json` around lines 23 - 33, The opencode.json "instructions" array
references files in the ignored memory_bank directory (entries under the
memory_bank folder), which will be missing on clean clones/CI; update the
"instructions" array to point to a tracked location or to a public read-only
mirror (or add a checked-in copy) so CI and new dev machines can access them
reliably, and ensure any loader falls back to an alternate source (env var or
URL) if those entries are unavailable.

@one-ea one-ea merged commit f4ddfa3 into main Apr 21, 2026
10 checks passed
@github-actions github-actions Bot mentioned this pull request May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs 文档更新 feature 新功能 frontend 前端 (React/Vite) 相关变更 styles CSS/UI 样式调整

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant