3baa0a87ba
fix(ui): --font-sans 补齐 CJK sans 字体栈
...
Tailwind v4 默认 --font-sans(ui-sans-serif, system-ui, sans-serif)不含中文字体。
后台 AdminLayout 根容器声明 font-sans 后,绕过 body 里精心配置的
'Noto Sans SC'/'PingFang SC'/'Microsoft YaHei' 栈,中文 fallback 到系统衬线体,
导致 /admin/* 页面中文(标题、列表项等)渲染成衬线体。
在 @theme 重定义 --font-sans,对齐 body 字体栈,根治全项目任何 font-sans 用法。
--default-font-family 指向 --font-sans,顺带强化 body 默认字体。
2026-07-09 23:37:16 +08:00
1584e01425
chore(deps): update all cargo and pnpm dependencies to latest versions
2026-07-09 18:12:54 +08:00
ecd5b441a0
fix(code-runner): 补齐可运行块上方间距
...
上一提交只加了 mb(下间距),但上间距仍缺失:PostContent 把每个 HTML
片段包在独立 <div> 里(post_content.rs:162),代码块前的段落成了该 div
的 :last-child,命中 .md-content p:last-child { margin-bottom:0 },
margin 折叠后上方间距归零。
把 mb 改成 my,var() 取同样的 --content-gap-paper(20px),由 CodeRunner
自身的 margin-top 保证上间距,不再依赖上方段落的 margin 是否被 :last-child
规则清零。
2026-07-09 18:03:30 +08:00
a4297355b5
refactor(router): downcast ServerFnError in ErrorLayout and improve fallback styling
2026-07-09 17:36:38 +08:00
6492e3c902
fix(code-runner): 可运行代码块与正文之间缺少间距
...
CodeRunner 根 div 没有任何垂直外边距,而普通代码块经 .md-content pre
规则带 margin-bottom: var(--content-gap-paper)(20px)。可运行块根元素是
div 而非 pre,该规则不生效,导致可运行块紧贴其后正文——上方靠前段落
margin 折叠勉强可见,下方完全无间距。
给根 div 加 mb-[var(--content-gap-paper)](沿用组件既有的 var() 任意值
写法),与普通代码块、段落保持一致的 20px 间距。
2026-07-09 17:36:22 +08:00
8cf34769e5
feat(not_found): commit HTTP 404 status code during SSR
2026-07-09 17:33:19 +08:00
1b38162ca4
feat(post_detail): propagate NotFound and other load errors to ErrorBoundary
2026-07-09 17:31:49 +08:00
37e8f51fac
feat(router): wrap public routes in ErrorLayout with ErrorBoundary
2026-07-09 17:29:47 +08:00
b5a64f8dcf
fix(post): 上下篇切换后可运行代码块消失
...
承接前两轮修复:路由重跑(79978aa)、正文更新(f86cb48)后,翻页时文章正文
正确切换了,但可运行代码块(CodeRunner)消失——容器 div 还在,CodeMirror
编辑器没挂载。
根因:上下篇切换(/post/rust → /post/go)时 PostContent 组件被复用(仅重渲染,
非重新挂载)。其内部 CodeRunner 用片段索引作 key(runner-{i}),而两篇文章的
代码块索引恰好相同(都是 1/3/5),keyed diff 按相同 key 复用 CodeRunner 实例。
复用的实例保留 use_hook/use_effect 状态:挂载 use_effect 的「防重复 init」守卫
(editor_handle.is_some())阻止 CodeMirror 挂载到新的(已替换的)DOM 容器,
而旧 CodeMirror 还绑定在被销毁的 rust DOM 上。同时 PostContent 自身的 use_effect
(__initPostContent 复制按钮 / 灯箱初始化)也不重跑,新文章交互脚本不初始化。
曾尝试直接给 PostContent 加 key=post.slug,无效:Dioxus 的 key diff
(diff_keyed_children)只在「兄弟节点列表」里生效,对单个非列表元素的 key
变化走 diff_non_keyed 路径、按位置复用,不触发 remount(经 use_hook 探针确认:
翻页后 CodeRunner 函数体执行了但 use_hook 从未重新执行)。
修复:把 PostContent 包进单元素 keyed 列表(for + iter::once + key=slug),
使其进入 keyed diff 路径——slug 变化时旧实例移除、新实例创建,连带 CodeRunner
重新挂载、交互脚本重新初始化。
验证(headless chromium,rust↔go 各含3个代码块):
- 修复前:翻页后容器在、CodeMirror 挂载数=0(红灯)
- 修复后:CodeMirror 挂载数=3,骨架屏清零;prev/next 双向 + SSR 首屏均正常
2026-07-09 17:12:48 +08:00
2498cd9c0b
docs(pitfall): 记录反应式 hook 不追踪普通 prop 的陷阱
...
将本轮三轮调试(commits 79978aa / 1c56fd8 / f86cb48)踩到的同一 Dioxus 0.7
陷阱写入 Pitfall Log,避免后续重复踩坑:
use_server_future / use_resource / use_memo 只在闭包内读取的 signal 变化时
重跑/重算(经 ReactiveContext 订阅)。普通组件 prop(String/i32 等非 signal)
被 move 进闭包后是冻结快照,读取它不建立订阅——prop 变化时 hook 不刷新,
组件虽重渲染但 hook 永久保留首次值。
该陷阱只在「同路由变体间导航」(/post/a → /post/b)触发:复用组件实例、仅
更新 props;跨变体导航会新挂载组件、hook 首次运行,掩盖问题。
记录两个真实案例(路由取数 use_server_future、正文解析 use_memo)的根因与
修复,给出判断规则(signal 依赖安全 / prop 依赖会静默过期 / prop 派生值
优先内联而非 use_memo),并标注 CommentSection 依赖 article key remount
才安全的潜在隐患。
2026-07-09 16:43:33 +08:00
f86cb48efe
fix(post): 上下篇切换后正文内容不更新
...
承接 fix(post) 的路由重跑修复:标题/描述更新后,正文仍停留在旧文章。
根因:PostContent 用 use_memo 缓存 split_content_fragments 的结果,但 memo
依赖 ReactiveContext 追踪闭包内读取的 signal 才会重算。content_html 是普通
String prop,读取它不建立订阅——memo 永久缓存首次解析结果。上下篇切换时
content_html prop 已是新文章的 HTML,但 memo 返回旧 fragments,dangerous_inner_html
收到旧 html 字符串,diff 判断属性值未变 → 不生成 setAttribute 编辑 → 正文 DOM
停在旧文章。
标题/描述之所以正常更新:它们直接读 post(来自 use_server_future 的 Resource,
是 signal),不走 memo。
修复:去掉 use_memo,直接在 render 内调用 split_content_fragments(纯函数,
符合渲染纯净性)。每次渲染用当前 content_html 重新解析,diff 收到新 html →
正文 DOM 正确更新。
验证(headless chromium):
- 修复前:/post/rust 点 Next,标题变「写文章页面改为左右布局」,正文仍是
rust 的「第一步:定义世界」(红灯,复现)
- 修复后:正文随之变为「问题」(绿灯);prev 方向同样通过;首次 SSR 正常
2026-07-09 16:39:41 +08:00
1c56fd8ec1
fix(home,tags): 同变体路由分页/切标签后列表不更新
...
延续上一个 commit(fix(post))发现的同类 bug,统一修复 home 和 tags:
- home: /page/1 → /page/2(同 Route::HomePage 变体)后文章列表不更新
- tags: /tags/a → /tags/b(同 Route::TagDetail 变体)后标签下文章不更新
根因完全一致:use_server_future 闭包内读取的 current_page / tag 是普通 prop
(i32 / String),move 进闭包后成为冻结快照,不建立反应式订阅,同变体导航
复用组件实例时 future 不重跑。
注意 / → /page/2(Route::Home → Route::HomePage,跨变体)不受影响,因为跨
变体会新挂载组件、future 首次运行;只有同变体间的 prop 变化才触发 bug。
修复:闭包内通过 router().current::<Route>() 读取当前 page/tag 建立订阅。
Home(/ 路由,Route::Home 变体无 page 字段)调用 HomePosts 时走兜底分支用
传入的 current_page。
验证(headless chromium,本地设 APP_BASE_URL 绕过 CSRF 403):
- home 修复前:/page/1 点下一页,URL 变 /page/2,首篇仍是 page1 内容(复现)
- home 修复后:首篇随分页变为 page2 内容
- tags 因数据库无多文章标签,无法端到端验证;代码模式与 home 一致且编译通过
2026-07-09 16:22:08 +08:00
79978aa4a4
fix(post): 修复上/下一篇导航后文章内容不更新
...
点击文章底部的上一篇/下一篇后,URL 正确变化为 /post/<new-slug>,但页面
内容仍停留在旧文章,只有刷新浏览器才生效。
根因:use_server_future(内部即 use_resource)依赖 ReactiveContext 追踪
闭包内读取的 signal 来决定重跑时机。但 PostDetail 的 slug 是路由宏注入的
普通 String prop,move 进闭包后成为冻结快照,读取它不建立任何反应式订阅。
同一 Route::PostDetail 变体间的导航(/post/a -> /post/b)会复用组件实例、
仅更新 props,而 use_server_future 的 task 只在首次 hook 时创建一次,因此
永远不会重跑。
此前 commit 225bb24 把 slug_signal 镜像 + render 期 set 改成直接读 prop,
消除了反模式,但也丢掉了唯一能触发重跑的订阅源(slug_signal 的读取),
反而引入了这个 bug。
修复:在闭包内通过 dioxus::router::router().current::<Route>() 读取当前
slug。current() 内部调用 subscribe_to_current_context(),在 use_server_future
的 ReactiveContext 中注册订阅,路由变化即触发重跑。render body 保持纯净。
验证(headless chromium,本地需设 APP_BASE_URL 绕过 CSRF 403):
- 修复前:点 Next 后 URL 变 /post/1783580058,标题仍为旧文章(复现 bug)
- 修复后:URL 变,标题随之变为新文章
2026-07-09 15:34:25 +08:00
6a3ac038da
refactor(write): 摘要字段移至右侧栏
...
摘要从左栏(标题下方)移到右栏侧边栏,作为独立分节位于标签与封面图之间,
与链接/标签/封面统一为带边框输入框的分节式布局。骨架屏同步更新。
2026-07-09 14:45:15 +08:00
19b47b11a4
style(admin): 缩小后台菜单左右边距
...
aside 容器 p-6 → p-4,菜单整体向两侧收紧
2026-07-09 14:36:57 +08:00
6e12b33769
fix(write): 彻底修复骨架屏高度不撑满
...
骨架屏在两条渲染路径都被高度链断裂卡住,内容只占页面 40-50%,
底部大片空白。
根因:高度链在两处父容器断裂,导致骨架屏无法引用到确定高度:
1. 未登录态(admin_layout):骨架屏被包在 div.p-10.animate-pulse,
该 div 是普通块级元素,无 flex-1 不撑满 main、非 flex 容器使
骨架屏 flex-1 无效、无 h-full。SSR 初始态走这条。
2. 登录态(write.rs 覆盖层):absolute inset-0 虽撑满,但非 flex
容器,骨架屏 flex-1 无意义;改 h-full 又遇 height:100% 在
非显式高度父容器下解析不可靠的经典坑。
修复:统一让两条路径的父容器都成为 flex flex-col,骨架屏根用
flex-1(比 height:100% 可靠,不依赖父显式 height):
- admin_layout 包裹层: p-10.animate-pulse → flex-1.min-h-0.
flex.flex-col.animate-pulse(去掉对 write 不合适的 p-10,
非 write 页面 padding 由 main_class 自带)
- write.rs 覆盖层: absolute inset-0 → absolute inset-0 flex flex-col
- write_skeleton 根: h-full → flex-1
2026-07-09 14:29:34 +08:00
e6ce87b315
fix(write): 修复骨架屏高度不足
...
骨架屏被放在 write.rs 的 absolute inset-0 覆盖层里,但根容器
用了 flex-1——在非 flex 父层里 flex-1 无效,导致骨架屏不撑满
高度,编辑器区域被压缩,底部留出大片空白。
改 flex-1 为 h-full,让骨架屏根撑满覆盖层高度,内部 flex
flex-col 再正常分配编辑器 flex-1 的高度。
2026-07-09 14:20:58 +08:00
1716976d91
refactor(write): 骨架屏同步为左右两栏布局
...
骨架屏原为单列结构(顶部标题+元信息 → 编辑器 → 底栏),
与重构后的页面布局脱节。重写为镜像真实页面:
- 两栏容器:左 flex-1 + 右 w-80 border-l
- 左栏:标题 + 摘要 + 编辑器(flex-1 撑满,含工具栏+正文行)
- 右栏:链接/标签/封面三节(p-6 + border-b 分隔线)
- 底栏:px-6 border-t,圆角按钮占位
这样加载瞬间骨架屏与真实内容对齐,不会出现布局跳变。
2026-07-09 14:11:48 +08:00
aba2aa2105
style(write): 移除右侧栏背景,回归透明
...
侧边栏不再用 paper-entry 整栏底色,改为透明(与左栏共用
paper-theme 底),靠 border-l 和节间分隔线划分区域。
同步:输入框改回 paper-entry 底 + 实色边框,封面图拖拽区
也改回 paper-entry,保持与透明侧栏的层次对比。
2026-07-09 14:08:00 +08:00
abc130eae7
refactor(write): 重新设计右侧栏为分节式轻量布局
...
原「卡片套卡片」结构(slug+标签包在一个重卡片里、封面图独立卡片)
层级冗余、视觉笨重。改为分节式(section)布局:
- 侧边栏整体用 paper-entry 底色,与左栏 paper-theme 形成层次区分
- 链接/标签/封面图三节用细分隔线 border-b 分隔,而非独立卡片
- 标题改用 uppercase 小标签 + tracking-wide,更专业紧凑
- 输入框改用 paper-theme 底 + 透明边框,聚焦时显主色边框
- 封面图拖拽区底色改 paper-theme,与侧边栏背景对比清晰
移除不再使用的 META_LABEL_CLASS 常量。
2026-07-09 14:03:07 +08:00
9b39e972e7
style(admin): 收紧后台所有页面左右边距 px-10 → px-6
...
px-10(40px) 偏宽,收紧到 px-6(24px) 更紧凑。
- admin_layout 的 main_class: 影响 dashboard/comments/posts/
posts_trash/runner/system 等所有非 write 页面
- write 页面左栏内容区 + 底栏,保持与其它页面一致对齐
2026-07-09 13:52:29 +08:00
012c21532d
refactor(write): 移除「撰写新文章/内容编辑器」页头条
...
页头条占用了顶部垂直空间且与左侧栏的标题输入框语义重复,
直接移除,让写作区从顶部开始,编辑器获得更多高度。
2026-07-09 13:50:46 +08:00
7b678481c6
refactor(write): 写文章页改为左右两栏布局,编辑器自适应高度
...
原单列布局把编辑器挤到折叠线以下、又被元信息卡片压缩(h-[60vh]),
显得太矮。改为左右两栏:
- 左栏(主写作区): 标题 + 摘要 + 编辑器,编辑器改为 flex-1 撑满
剩余高度(内部 .tiptap-editor/.ProseMirror 已是 height:100%)。
- 右栏(侧边栏, w-80): Slug + 标签 + 封面卡片纵向堆叠,独立滚动。
- 页头条提到两栏之上,底栏(取消/状态/发布)不变,仍横跨两列贴底。
#tiptap-editor 容器 id 保持不变,tiptap_bridge 按 id 挂载不受影响。
所有信号/server function/状态逻辑零改动,纯结构样式调整。
2026-07-09 13:40:37 +08:00
e8fc255b19
fix(code-runner): 文章页运行结果添加最大高度与滚动
...
输出区原本无高度限制,大量 stdout 会撑开整个卡片/文章页。加 max-h-80
(320px) 与 overflow-auto,超出后垂直滚动。overflow-x-auto 升级为
overflow-auto 以同时支持垂直滚动。与编辑器内 resultArea 的 300px 上限
保持一致。
2026-07-09 13:28:04 +08:00
1521414cad
fix(code-runner): 修复编辑器内运行结果不换行
...
resultArea 挂在 ProseMirror 可编辑文档树内部,浏览器对 contenteditable
子树强制 white-space 折叠(computed 为 collapse),CSS 的 white-space:
pre-wrap 被覆盖,导致 textContent 里的 \n 与行首空格被吞、多行输出挤成一行。
改用 <br> 显式渲染换行、\u00A0 替换空格/制表符以保留对齐——二者均不依赖
white-space。createTextNode 避免 stdout 含 HTML 标签时的注入。
2026-07-09 13:25:59 +08:00
88558d73d3
fix(markdown): update unsupported-lang test to use ruby
...
上一个提交为 code runner 加了 Rust 支持,rust 进入语言白名单后,
render_markdown_runnable_marker_on_unsupported_lang_ignored 里的
rust 用例失效(现在 rust 确实会挂 data-runnable)。改用确实不在
白名单的 ruby 作为不支持语言的样本。
2026-07-09 11:50:57 +08:00
60bfee7f36
feat(build): inject git/rustc/build-time info and print on startup
...
新增 build.rs 在编译期采集构建元信息(version / git describe / commit
hash / 提交时间 / rustc 版本 / 编译时刻),通过 cargo:rustc-env 注入,
由 src/build_info.rs 用 env! 宏在编译期内联读取,零运行时开销。
启动时(main() tracing 初始化之后)调用 log_build_info() 分 4 行打印
这些字段,方便定位线上跑的是哪个二进制。
设计取舍:
- 只用 std,不引 vergen/git2 等 build-dependencies(git 不可用时降级为
"unknown",不 fail the build)。
- build.rs 存 Unix 秒,运行时用已有的 chrono 解析回 RFC3339。
- 声明 rerun-if-changed=.git/HEAD,确保切换提交后重新采集。
2026-07-09 11:50:39 +08:00
0ab3340133
fix(code-runner): allow exec on /tmp tmpfs for compiled languages
...
Docker tmpfs 挂载默认带 noexec,编译型语言(go/rust)把编译产物
落在 /tmp 后再 exec 会报 permission denied:
- rust: /tmp/main (run-rust.sh 的 rustc 输出)
- go: /tmp/go-build*/b001/exe/main (GOTMPDIR=/tmp 的链接器输出)
解释型语言(python/node)执行根文件系统里的解释器,不在 tmpfs 上,不受影响。
给 /tmp tmpfs 显式加 exec 选项。这是容器执行的固有需求——只要
沙箱支持任何编译型语言,/tmp 就必须可执行。安全模型由只读根文件系统 +
cap_drop=ALL + no-new-privileges + pids/memory/cpu 限制兜底,/tmp 可执行
不引入额外攻击面(容器内本就允许执行自身进程)。
2026-07-09 11:04:27 +08:00
2f6f8e549d
refactor(code-runner): default CODE_RUNNER_LANGUAGES to all-open
...
CODE_RUNNER_LANGUAGES 未设置时,白名单不再默认为 python,node,
而是放行 LANGUAGES 注册表里的全部语言。该环境变量变为「可选的收窄开关」:
新增语言到注册表即可默认启用,无需再同步运维白名单(避免两边脱节,
如本次 go/rust 就因 .env 未同步白名单导致 is_supported_lang 拒绝)。
- RunnerConfig.languages: Vec<String> → Option<Vec<String>>,
None 表示不限制
- is_supported_lang: Option 为 None 时直接放行注册表语言
- 测试 is_supported_lang_default_whitelist → is_supported_lang_default_all_open,
断言 go/rust 在默认(未设白名单)下可用
2026-07-09 11:02:04 +08:00
75767661ac
feat(admin): surface Go and Rust on the runner page
...
SUPPORTED_LANGS 追加 go / rust,并为两者补 default_source 示例源码,
使新增语言在 /admin/runner 试运行沙箱的语言切换按钮中可见、可用。
前端语言集合此前是注册表的手动镜像副本(注释已声明需对齐),
本次保持该约定;runner.rs:15 的注释依然准确。
2026-07-09 10:52:03 +08:00
e48e992ca1
fix(system): add key to tab content div to fix skeleton delay
...
Without a key, Dioxus 0.7 VDOM diffing may reuse hook slots from
the previous tab component on tab switch. This caused DelayedSkeleton's
visible signal to retain the old true value, bypassing the 200ms delay.
Adding key based on active_tab forces full unmount/remount on tab switch.
2026-07-09 10:22:29 +08:00
c624997c04
feat(code-runner): add Go and Rust language support
...
为 Code Runner 新增 Go 和 Rust 两种编译型语言,沿用现有 base 镜像分层结构。
Go:
- run_cmd 直接用 go run(单条命令,内部编译+运行)
- 镜像把 GOCACHE/GOTMPDIR/GOPATH 重定向到 /tmp tmpfs
(只读根文件系统下 /Users/issuser/.cache 不可写)
- default_limits: 1 核 / 256MB / 10s(编译冷启动比解释型慢)
Rust:
- rustc 编译 + 运行是两步,但 docker.rs 注入脚本用 exec 执行 run_cmd,
exec 替换 shell 进程后 'A && B' 后半段不执行,
因此镜像内置 /usr/local/bin/run-rust.sh wrapper 封装两步
- default_limits: 1 核 / 512MB / 15s(rustc 内存大、编译慢)
新增镜像与白名单后,默认 CODE_RUNNER_LANGUAGES=python,node,go,rust。
2026-07-09 09:40:32 +08:00
d690910efa
fix(admin): 消除骨架屏→认证→正常页面的布局闪烁
...
合并 (true, None) 和 (false, _) 两个分支为统一的 fallback 骨架屏。
旧 (true, None) 分支渲染无侧边栏的全屏居中文字「正在验证身份...」,
与骨架屏 / 正常页面的侧边栏+卡片布局完全不同,在 checked.set(true)
同步触发 re-render 时产生明显的布局跳变。统一后三个状态的布局结构
一致,过渡平滑无闪烁。
2026-07-08 18:23:08 +08:00
53de550d7c
fix(admin): 修复后台骨架屏不可见问题
...
- admin_layout: 移除 (false, _) 分支中的 DelayedSkeleton 包裹,直接渲染
骨架屏并附加 animate-pulse。DelayedSkeleton 依赖 use_effect 延迟 200ms
置 visible=true,但 SSR 不执行 effect,导致服务端 HTML 输出空节点;
hydration 后 checked 又会立即置 true 退出该分支,骨架屏窗口趋近于零。
去掉后 SSR HTML 直接包含骨架屏,用户在 WASM 加载 + 身份校验期间可见。
- comments: loading 初始值从 false 改为 true,与 use_paginated hook 保持
一致。旧值导致首帧 loading()=false,骨架屏条件永远不满足。
2026-07-08 18:20:25 +08:00
3eaf330673
fix(admin): use flex split layout for write page to fix sticky footer jump
...
The sticky footer with -mt-12 negative margin caused a jump when
scrolling to bottom: sticky bottom-0 positions against the scroll
container's padding box, but the negative margin pulls the element up
by 48px, so at scroll-end the footer shifts up and clips the editor.
Replace sticky+neg-margin with a proper flex split layout:
- AdminLayout: write route now gets overflow-hidden on the card and a
padding-free, non-scrolling flex main; other admin pages unchanged.
- write.rs: root becomes flex-col; content area is flex-1 overflow-y-auto
with the px-10 py-12 padding moved here; footer is a flex-shrink-0
sibling that sits at the card bottom permanently, never scrolls, never
jumps.
min-h-0 added on flex children so overflow can shrink below content
height (required for flex + overflow to work).
2026-07-08 18:14:23 +08:00
7c98eee285
fix(admin): remove redundant pt-8 on write page top spacing
...
After the shell unification, main already provides py-12 (48px) top
padding via the shared admin shell. The write page's own pt-8 (32px)
was a leftover from when it had no outer card, so it stacked to 80px —
much larger than other admin pages. Remove it to match.
2026-07-08 17:46:50 +08:00
25c3172b7c
fix(admin): unify write page shell with other admin pages + sticky footer
...
The write route had a separate shell in AdminLayout (main element acted
as its own rounded card + scroll container), while all other admin pages
used a dedicated rounded-card div wrapping a centered main. This caused
visible margin/spacing differences.
Changes:
- AdminLayout: remove is_write_route shell branching; all admin pages now
share the same rounded-card div + centered main shell.
- write.rs: drop redundant max-w-7xl mx-auto on root div (main already
provides it); make bottom action bar sticky bottom-0 with negative
margins to bleed to card edges and a translucent backdrop so scrolling
content doesn't bleed through.
2026-07-08 17:42:25 +08:00
5c37b17cb8
fix(admin): align runner page width with other admin pages
...
runner.rs was the only admin page using max-w-5xl (1024px) while all
others (system/comments/dashboard/posts/posts_trash/write) use
max-w-7xl (1280px). The page's own comment claimed to align with
dashboard/posts/system but the width class contradicted that.
2026-07-08 17:25:39 +08:00
d72495c002
refactor(admin): merge trash into /admin/posts as a URL-driven tab
...
回收站从独立路由 /admin/trash 合并为 /admin/posts 下的一个 tab,
消除「回收站」导航项,让文章管理(列表 + 回收站 + 自动清理配置)集中在
同一页面,符合「相关功能就近聚合」的组织原则。
路由(router.rs)
- 删除 Route::Trash / Route::TrashPage
- 新增 Route::PostsTrash (/posts/trash) 与 PostsTrashPage (/posts/trash/:page)
- 静态段声明在 :page 之前::page 为 i32 类型,天然排斥 'trash' 文本,
路由匹配安全且遵循文件内 static-first 约定
导航(admin_layout.rs)
- 移除「回收站」导航项
- 「管理文章」高亮覆盖其下所有子路由(列表/分页/回收站 tab),
顺带修掉原 PostsPage 分页不高亮的小 bug
页面(posts.rs / posts_trash.rs)
- 新增 PostsTabs:URL 驱动的 tab 栏(Link 切换路由,非本地 signal),
回收站 tab 带 get_post_stats().trash 数量角标,>0 用主题强调色提醒
- trash.rs → posts_trash.rs:TrashPage 重命名为 PostsTrashPage,
分页路由改指向 PostsTrash/PostsTrashPage,内容(列表/批量操作/
清空回收站/AutoPurgeSettings)原样迁移;include_str 测试同步更新
- posts.rs 的 PostsPage 渲染 PostsTabs,列表逻辑不变
无后端契约变更:所有 server function(list/delete/restore/purge/
batch/empty_trash/get_trash_settings)签名不变,Pagination 组件
接收具体 Route 变体,给 trash tab 传 PostsTrash 即可,无需改组件。
2026-07-08 16:52:02 +08:00
9c3f2242ba
feat(stats): add trash count to PostStats for posts page badge
...
为合并回收站到 /admin/posts 的 tab 设计铺路:PostStats 新增 trash 字段,
统计 deleted_at IS NOT NULL 的文章数,供文章列表页「回收站」tab 角标使用。
- stats.rs: 条件聚合查询增加 trash 列,server/非 server 构造同步补字段
- models/post.rs: PostStats 加 pub trash: i64
- cache.rs: post_stats_cache_roundtrip 测试 fixture 补 trash 字段
2026-07-08 16:40:32 +08:00
ebeec2a295
fix(comments): resolve reactivity dependency tracking in CommentSection hooks
2026-07-08 15:42:44 +08:00
a72a167a5f
fix(comment): resolve potential SSR hydration mismatch in CommentSection
2026-07-08 15:38:29 +08:00
74a74e77a8
feat(comment): render form and header persistently to eliminate layout shift
2026-07-08 15:32:24 +08:00
42548d5c3a
style(comment): redesign comment list skeleton to match real list layout
2026-07-08 15:30:32 +08:00
9e70fd9893
feat(runner): set Vim mode default to enabled in CodeRunner
2026-07-08 14:43:57 +08:00
cda5a92230
feat(runner): enable Vim mode toggle for CodeRunner sandbox
2026-07-08 14:37:52 +08:00
3bb67f6673
style(runner): optimize CodeMirror editor height auto-scaling and scrolling limit
2026-07-08 14:32:04 +08:00
f60bc4beaa
fix(runner): fix CodeMirror editing reversion feedback loop
2026-07-08 14:28:45 +08:00
61c64841f9
refactor(skeletons): 统一骨架屏延迟机制,200ms 内加载不显示
...
- DelayedSkeleton 从「延迟 pulse 动画」改为「延迟渲染」:
前 200ms 完全不渲染,超时后才显示并带 pulse 动画
- admin_layout 从 use_delayed_loading hook 改为 DelayedSkeleton 包裹
- CommentListSkeleton 的两处裸用(section.rs / post_detail.rs)
补上 DelayedSkeleton 包裹
- 移除不再使用的 use_delayed_loading hook 模块
2026-07-08 14:18:08 +08:00
5b9e65bf72
fix(editor): 修复文章页折叠行被撑高 & 折叠图标垂直不居中
...
文章页 CodeRunner 嵌在 .md-content 内,CodeMirror 折叠后插入的
<img class="cm-widgetBuffer">(width:0、height:1em 的占位符,用于对齐折叠
widget 基线)被 .md-content img 的 margin:1rem 0 命中,上下各 16px margin
把折叠那行 .cm-line 撑到 47.5px(试运行页不在 .md-content 内,正常 19.6px)。
对比两页 .cm-line 的 children 锁定:文章页含 cm-widgetBuffer,试运行页没有。
input.css:.md-content img 选择器加 :where(:not(.cm-widgetBuffer)) 排除占位 img,
用 :where() 压低特异性避免影响其它规则。
themes.ts:core 对折叠列 .cm-foldGutter .cm-gutterElement 完全没设样式,格子
高度 ≈ font-size(14px) 比正文行(line-height ≈19.6px)矮,图标偏离中央。给格子
flex 居中 + line-height:1.4(复刻 CM 的字号×1.4 行高算法),图标落在正文行中央。
2026-07-08 14:03:14 +08:00