197 Commits

Author SHA1 Message Date
xfy
575b6b533e feat(code-runner): CodeRunner 挂载 CodeMirror 编辑器,支持双向绑定
兑现组件一直承诺但无人实现的「调用方挂载编辑器」契约——此前容器 div 只
输出 id,admin/runner 沙箱与文章正文 runnable 块都只能提交写死的初始源码,
作者/读者无法编辑。

- CodeRunner 在 WASM 端按自身 container_id 调 codemirror_bridge.create
  挂载编辑器,onChange 回写内部 source_signal(编辑器内容 = 唯一真源)
- run_code 改读 source_signal 当前值,提交编辑后内容而非初始快照
- 主题切换经 use_resolved_theme 订阅,热切换编辑器主题
- use_drop 销毁 EditorHandle,释放 CodeMirror 实例与 closure 槽位
- source prop 外部变更(admin 切换语言重置示例)同步进 signal + 编辑器
- admin/runner 页面 overrides 解析挪进 use_memo,避免 render 体写状态

范式镜像 SQL 控制台(system.rs)与 Tiptap 编辑器(write.rs)。
2026-07-05 14:32:39 +08:00
xfy
16bd644783 fix(admin): 仪表盘空数据时显示空状态占位
空库下 get_post_stats / list_posts 正常返回,但 list_posts 返回
Some([]) 时走有数据分支、循环 0 次,渲染出一个空表格容器,视觉
上仪表盘近期文章区域一片空白,没有空状态引导。

复用项目既有的 empty_state::EmptyState 约定(与 posts.rs / trash.rs /
comments.rs 列表页一致):Some(posts) 且 posts.is_empty() 时渲染
暂无文章插画 + 写文章CTA。空状态放在 ADMIN_TABLE_CLASS 容器
之外,避免 overflow-hidden 裁掉插画的 py-20 内边距。
2026-07-04 22:49:25 +08:00
xfy
f3eb93f320 docs(agents): document code runner and fix clippy/biome lint
文档:
- AGENTS.md 新增「Code Runner」架构章节(三层架构 + WASM 可见性规则 +
  governor 0.8 无 per_day 的处理 + runner 镜像契约)
- Environment 段补齐 CODE_RUNNER_* 与 RATE_LIMIT_CODE_EXEC_* 环境变量

Lint 修复(clippy --all-targets --all-features -D warnings 全绿):
- runner.rs/runner.rs: use_signal 冗余闭包 → 直接传 fn
- runner_config.rs: &*RUNNER_CONFIG 去 deref;assert_eq!(...false/true) → assert!
- markdown.rs: 局部变量 /// 改 //;Option.map().flatten() → and_then
- languages.rs: 删除从未读取的 LanguageDef.name 字段(语言名即 map key)
- docker.rs: start_container if let Err → ?;timeout match → is_err();
  嵌套 if 合并(修 task 2 遗留 + clippy 1.96 新 lint)
- pages/admin/posts.rs: use_signal 冗余闭包(预存 lint,顺带修绿)
- codemirror editor.ts: biome format 重排 setSchema 单行
2026-07-03 18:57:25 +08:00
xfy
bbd4553467 feat(admin): add dedicated code-runner sandbox page at /admin/runner
不改动 852 行的 write.rs 编辑器布局,改为独立 admin 页面(与 posts/system 同款):
- runner.rs: Runner 页面(语言切换 join 按钮 + overrides JSON 输入 + CodeRunner 沙箱)
  切换语言自动填充示例源码;overrides JSON 实时校验,畸形时提示并忽略
- mod.rs: 注册 runner 模块并导出 Runner
- router.rs: 新增 #[route("/runner")] Runner {} 嵌套在 /admin 下
- admin_layout.rs: 导航新增「试运行」入口
2026-07-03 18:41:05 +08:00
xfy
46860f7c41 fix(ui): apply mount animation inside each page component to avoid skeleton interception 2026-07-03 17:24:38 +08:00
xfy
197c08d258 style(ui): fix inconsistent padding on post card 2026-07-03 17:13:02 +08:00
xfy
52b4f91622 fix(admin/write): restore scrollability by using overflow-y-auto and min-h-full 2026-07-03 16:50:53 +08:00
xfy
acfddb3e17 style(admin/write): overhaul write page to modern minimalist layout 2026-07-03 16:44:39 +08:00
xfy
d3a50728ed style(admin): overhaul layout to modern minimalist sidebar design 2026-07-03 15:50:59 +08:00
xfy
38ba6692d3 style(admin): redesign admin UI with soft rounded corners and Catppuccin theme 2026-07-03 15:46:18 +08:00
xfy
9f69a132fa refactor(admin): redesign write page for industrial aesthetic 2026-07-03 15:37:41 +08:00
xfy
cad0e3d83b fix(admin): restrict write page width to match other pages 2026-07-03 15:35:06 +08:00
xfy
d1041a84dd feat(admin): complete redesign of backend ui with industrial minimalist aesthetic 2026-07-03 15:32:02 +08:00
xfy
45f4e9cde0 refactor(ui): 去除 Rust 中硬编码颜色,统一语义色阶
跟随 Catppuccin 配色迁移,清理 src/ 中绕过 paper-* 语义 token 的
硬编码 dark:[#hex] 任意值,改用 Tailwind 默认灰阶(gray-700/800),
它们随主题切换自然适配。

- post.rs 状态徽章:dark:bg-[#333]/dark:text-[#9b9c9d] → gray-800/gray-400/500
  (同步更新测试断言)
- comments/* 与 skeletons/* 的 dark:[#2a2a2a]/[#2e2e33]/[#333]/[#5a5a62]
  → 对应 gray-600/700/800
- trash.rs 状态灯发光圈 rgba 由旧鼠尾草绿(92,122,94) 改 Latte green(64,160,43)
- ui.rs BTN_SECONDARY 注释更新为新 Teal 强调色值

注:src/pages/admin/posts.rs 的 clippy redundant_closure 报错为既有问题,
与本次配色迁移无关,不在范围内。
2026-07-03 14:25:40 +08:00
xfy
64ad40ca61 fix(admin/posts): Tooltip 包裹后操作按钮恢复垂直居中
- Tooltip 外层 div 由块级改为 inline-flex,作为行内伸缩盒参与
  flex 行对齐,不再撑高或破坏基线
- 操作列容器补 items-center,显式锁定所有子项垂直居中
2026-07-03 11:08:04 +08:00
xfy
73b9cad550 refactor(ui): 抽取 Tooltip 组件,行内重建按钮补 tooltip
新增可复用 Tooltip 组件(group-hover 显现,支持 top/bottom 朝向),
消除顶部重建按钮手写的内联 tooltip div 重复,并为表格行内重建
按钮补上 tooltip(朝上,避免被表格容器的 overflow-hidden 裁掉)。
2026-07-03 11:03:56 +08:00
xfy
b6fe0ae082 style(admin/posts): 删除按钮 loading 用 spinner 覆盖文字
- 删除改为非乐观:点击后保留行并显示 loading,服务端成功才移除行、
  失败保留行并弹提示(顺带修掉原先「删除失败行却没了」的隐患)
- deleting 信号由 Option<i32> 改为 HashSet<i32>,支持并发删除
- 删除按钮 loading 样式与重建一致:文案恒为「删除」,spinner
  居中覆盖、文字 opacity-40 衬底
2026-07-03 10:51:16 +08:00
xfy
1530d3a2f4 style(admin/posts): 顶部重建按钮 loading 用 spinner 覆盖文字
工具条「重建内容」「重建全部」按钮 loading 时由「重建中...」文案
改为 spinner 居中覆盖、文字 opacity-40 衬底,与行内重建按钮的
loading 样式统一。
2026-07-03 10:46:57 +08:00
xfy
db87d3c886 fix(admin/posts): 重建支持并发 loading,spinner 覆盖文字
- rebuilding 信号由 Option<i32> 改为 HashSet<i32>,连续点击多篇
  文章的重建按钮可同时处于 loading 态(单值会被后点的覆盖)
- loading 样式调整:按钮文案恒为「重建」,spinner 绝对定位覆盖在
  文字上方居中,文字 opacity 调至 40% 衬底,不再切换文案
2026-07-03 10:40:11 +08:00
xfy
a5c00caa80 style(admin/posts): 操作列加宽横排,重建按钮 loading 用 spinner 图标
- 操作列宽 w-24 → w-44,三个按钮一行排开不换行
- 重建按钮 loading 态由「重建中...」文案改为 spinner 图标 + 「重建中」
- spinner 复用 public/icons/90-ring-with-gradient.svg,作为 SPINNER_SVG
  常量收入 ui.rs(修正原 SVG 损坏的孤立 ':' 选择器,重命名 id/类名
  避免多处内联时冲突),用 currentColor 继承按钮文字色
2026-07-03 10:26:14 +08:00
xfy
e55f2e12bd refactor(admin/posts): 重建按钮文案改「重建」并去掉完成弹窗
按钮态文案与原有一致(「重建中...」);on_rebuild 改为静默执行,
仅按行切换 rebuilding 按钮态,完成后不再弹浏览器 alert。
2026-07-03 10:16:13 +08:00
xfy
0fe0387ea3 feat(admin/posts): 文章列表操作列新增「重建内容」按钮
新增 rebuild_post_content_html(post_id) server function,从 DB 读取
content_md(事务内 FOR UPDATE 锁行)重新渲染 content_html/toc_html
并更新 word_count/reading_time,按影响范围精准失效列表、slug 单篇
与搜索缓存。

PostRow 操作列在「编辑」「删除」之间插入「重建内容」按钮(鼠尾草绿
文字样式),按行禁用并切换「重建中...」文案,成功/失败弹出浏览器
提示,与删除按钮的反馈模式一致。

为避免与单篇 rebuilding 信号冲突,将原批量重建工具条信号 renaming
为 batch_rebuilding。
2026-07-03 10:13:15 +08:00
xfy
65b15509a7 chore(wasm): 清理 wasm32 target 下残留的 clippy lint
这些 lint 在 server build(默认 --all-features)下被 cfg 剥离故未暴露,
仅 wasm32 web-only target 触发。逐项清理:

- footer.rs: 两处 redundant closure(web_sys::window 直接传函数引用、
  sync_visible 直接传闭包)+ let _ = unit 绑定(scroll_to_*)。
- hooks/event_listener.rs: 复杂类型 Rc<RefCell<Option<(Closure,T)>>>
  抽 type ListenerState<T> 别名。
- system.rs: 两处单模式 match { Ok=>.., _=>{} } 改 if let。
- write.rs: split(|c| matches!(c,...)) 改 split([char; N]) 数组模式。
- codemirror_bridge.rs: let _ = instance.destroy() 删 unit 绑定,
  与 tiptap_bridge.rs 同处写法对齐。

wasm32 与 server 两 target clippy -D warnings 均通过。
2026-07-02 17:15:27 +08:00
xfy
59359b18ce refactor(hooks): 新增 use_paginated 并迁移 posts/trash 分页加载
按 Dioxus 0.7 custom hooks 范式,把后台分页列表页反复出现的
use_effect + spawn + (loading/items/total/error) 三件套收口到
use_paginated 组合式 hook。统一非 wasm cfg 处理(SSR 不发请求、
loading 立即置 false),调用方只关心 fetch 闭包与页码依赖。

迁移范围(按降级预案,只做最规整的两处):
- pages/admin/posts.rs:顺手补 error signal(原先吞掉 Err),向
  trash 看齐,消除一致性债。
- pages/admin/trash.rs:直接替换,error 已有。

保留手写的形态(强行套 hook 反而更复杂):
- comments.rs:依赖 (page, status) 双 signal,status 是额外维度,
  套不进 (page, per_page) 模型;改 hook 会为单一调用点变复杂。
- system.rs 三个 tab:load_once + use_future 轮询形态,与 hook 的
  单次加载模型不契合。
- dashboard.rs:多源聚合(3 个并发 spawn 分别 set 不同 signal)。

list_posts / list_deleted_posts 是 server function,两端都生成
(wasm 端 client stub、server 端真实实现),故去掉原 cfg import。
2026-07-02 16:30:47 +08:00
xfy
62bd281684 fix(admin/posts): 重建结果消息改为独立行,修复悬浮溢出表格的错位
RebuildCacheBar 的「已重建 X 篇文章」结果消息原用 absolute top-full mt-2
悬挂在按钮行下方,但 mt-2(8px) + text-sm(20px) = 28px 大于外层 space-y-6
的 24px 间距,消息底边向下溢出 4px 侵入表格容器上沿;横向 left-0 又使消息
落在右上角按钮组正下方、覆盖状态/日期/操作列,语义位置错位。

将 rebuilding / rebuild_result 状态上提到 PostsPage:
- 结果消息在 header 与表格之间独立成行,进入文档流,吃 space-y-6 正常间距,
  既不撑高 header 触发 items-center 重排(曾用 absolute 规避的旧问题),
  也不脱离流溢进表格(absolute 引入的新问题)。
- RebuildCacheBar 改为接收父级 Signal props,只渲染按钮行本身。
2026-07-02 14:19:06 +08:00
xfy
eacc79ed83 feat(theme): 新增跟随系统主题模式与三态循环切换
Theme 枚举从 {Light, Dark} 扩展为 {Light, Dark, System},新增
ResolvedTheme 表示实际生效明暗。

- System 模式移除 localStorage 持久化,首屏防闪烁脚本因此自动回退到
  prefers-color-scheme 分支,首屏零闪烁。
- use_theme_provider 提供 Theme + ResolvedTheme 双 context;resolved 是
  theme 与 system_dark 的派生 memo。WASM 端注册 matchMedia change 监听,
  系统偏好变化时实时同步 .dark class 与下游(CodeMirror 等)。
- ThemeToggle 由二态 toggle 改为三态循环(Light→Dark→System→Light),
  图标按当前模式切换(太阳/月亮/显示器);仅当实际明暗翻转时才触发
  圆形展开动画,避免 System 与同向 Light/Dark 间无意义动画。
- system.rs CodeMirror 传参改读 resolved,使 System 模式下系统偏好变化
  时编辑器主题自动跟随。
2026-07-02 11:35:28 +08:00
xfy
9a218db478 refactor(assets): 编辑器背景图缩小至 w-24 并贴近右下角
原图无宽度约束,按原始尺寸渲染占满编辑器;现固定 w-24(与评论区
一致)并用 bottom-2 right-2 定位到右下角。一并提交新增的线条小狗
素材图与图标资源。
2026-07-02 11:22:34 +08:00
xfy
48a21564c4 refactor(assets): 背景图固定 opacity-10,移除动态透明度逻辑
动态判断不生效,统一固定为 10% 透明度,移除聚焦/有内容的条件类。
2026-07-02 11:17:41 +08:00
xfy
fe22d6dad8 feat(editor): tiptap 编辑器有内容时自动调淡背景图透明度
无内容时背景图保持 60% 透明度;当编辑器内有内容(非纯空白)时,
背景图淡出到 10%,与聚焦时行为一致。content signal 由 onUpdate
回调实时维护,输入即触发重渲染,无需额外状态。
2026-07-02 11:11:04 +08:00
xfy
48107d8e18 refactor(assets): 评论区与后台编辑器背景图换用 xiantiaoxiaogou_input_bg 2026-07-02 10:58:52 +08:00
xfy
9cf0e605d4 fix(admin): 修复服务器/数据库状态自动刷新失效 Bug
use_future 旧实现在同步闭包体内捕获 status/loading/error signal,
导致这些 signal 每次 .set() 后都触发 use_future 重建新 loop,
旧 loop 同时继续运行,造成并发 loop 指数增长(请求爆炸),
实际效果等同于自动刷新完全失效或行为异常。

修复:采用 Dioxus 0.7 官方推荐模式——
- use_future 闭包直接返回 async move {},不在同步层读任何 signal
- loop 内每次迭代读 refresh_interval/refresh_ms 的实时值
- 手动模式(interval = 0)改为 200ms yield + continue,
  使切换到自动模式时最多延迟 200ms 生效,避免忙等
- sleep 结束后二次检查 interval,防止 sleep 期间用户切回手动时
  仍然发起一次无意义请求

同时修复 DbStatusTab 和 ServerStatusTab 两处。
2026-07-01 17:09:39 +08:00
xfy
afa6bf892e feat(register): 输入框支持回车键提交表单
注册页 4 个输入框(用户名/邮箱/密码/确认密码)原先 onkeydown 为 None,
无法用回车提交。改为监听 Enter 触发 on_submit,与登录页行为一致。
2026-07-01 17:00:58 +08:00
xfy
7de97d8222 fix(write): 编辑模式回显空白由 effect 竞态导致
编辑文章时编辑器空白。根因:edit_post 由 None→Some 时,回填 effect
(写 content signal)与初始化 effect(读 content signal)在同一 tick
触发,无保证先后顺序。初始化 effect 经常先跑,读到 content()= 跳过
setMarkdown,且 editor_content_set=true 永久阻止重试。

修复:初始化 effect 直接从 edit_post(真值源)取 content_md 回填,不再
依赖 content signal——后者是派生值,与回填 effect 存在竞态。两个 effect
都已在 edit_post 上订阅,回填时读取它没有竞态。
2026-07-01 17:00:58 +08:00
xfy
26d37f7271 refactor(admin): use FilterTabs in /admin/system
system.rs 的 tab 栏从 Tabs 改用既有 FilterTabs,视觉与评论页完全一致
(平滑滑动指示条 + 选中文字 text-paper-primary)。Props 形状相同,仅改组件名;
SystemTab::as_str/from_str 桥接与枚举 match 穷举逻辑不变。
2026-06-30 18:05:05 +08:00
xfy
bf91d18c4f refactor(admin): use shared Tabs component in /admin/system
移除 System 组件内联的 tab 按钮,改调公共 Tabs 组件。
active_tab signal 经 SystemTab::as_str/from_str 与 Tabs 的 String API 桥接,
match 穷举面板选择逻辑不变。视觉/行为零变化,只消除重复。

同时移除 Task 1/2 加的临时 #[allow(dead_code)](as_str/from_str/Tabs 现已有调用方)。
2026-06-30 14:29:05 +08:00
xfy
ef75631889 feat(database): add SystemTab::as_str/from_str string bridge
为后续用基于 String 的 Tabs 公共组件替换内联 tab 做准备。
as_str/from_str 是纯函数双向映射,带 roundtrip/稳定 key/拒绝未知输入三类单测。
2026-06-30 14:13:54 +08:00
xfy
0b6a076d78 feat(database): use real COUNT(*) for small tables, fall back to estimate for large
row_estimate came from pg_class.reltuples, which is -1 for tables that
have never been ANALYZEd (the common case on a fresh/small DB), so the
admin table list showed -1 for every table — meaningless.

Replace row_estimate with row_count + row_count_estimated:
- total_size < 100MB: SELECT count(*) (real value, cost negligible on
  small tables; identifier quoted via PG escaping to stay injection-safe)
- total_size >= 100MB: fall back to reltuples estimate, flagged so the UI
  can prefix with ~

Frontend now shows real counts (1/2/14...) for small tables and ~N for
large ones; header copy updated to reflect the mixed source.
2026-06-30 13:57:37 +08:00
xfy
ecf3c73715 fix(database): make src/api/database compile under WASM (web-only) build
The /admin/system feature was never verified against the WASM frontend
target (dx serve builds src/main.rs with --features web and no server).
The entire src/api/database/ tree unconditionally pulled in server-only
crates (axum, sqlparser, dashmap, tokio, tokio_postgres, futures, regex)
and server-gated helpers (get_current_admin_user, parse_session_token,
get_user_by_token), so the frontend build failed with 43 errors.

Root cause + fixes (mirror the established settings.rs pattern):

- Gate server-only  imports behind #[cfg(feature = server)] in
  status/system_status/sql_console/schema/tasks/backup. The #[server] macro
  strips call sites but NOT use statements, so unresolved imports remained.
- Ungate  in main.rs: SystemSnapshot is a shared serde
  type referenced by ServerStatus.host on both targets; only the sampler
  task/SNAPSHOT static are internally server-gated.
- Gate  (pure Axum handler, zero WASM consumers) and the
  download_backup Axum handler in backup.rs.
- Move restore_backup's validation preamble (regex/backup_path/fs) inside
  the #[cfg(feature = server)] block; gate std::path/chrono::Utc imports.
- Add [[bin]] required-features=[server] for generate_highlight_css so the
  syntect-dependent build tool is skipped in web-only builds.

Genuine WASM bugs surfaced once the server bodies stopped masking them:

- system.rs: Closure import path (dioxus::prelude::wasm_bindgen ->
  wasm_bindgen, matching write.rs); setTimeout expects i32 not u32;
  editor_handle() -> editor_handle.read() (Signal is not Fn in 0.7).
- codemirror_bridge.rs: set_schema extern took &SqlSchema but SqlSchema is
  a serde type with no IntoWasmAbi. Changed both set_schema signatures to
  &JsValue; call site now serializes via serde-wasm-bindgen::to_value.
- system.rs: signals .set() inside spawn/use_future/onclick closures need
   bindings (incl. the *_f rebinding copies); added cfg_attr
  allow(unused_mut) on the 4 tab components, matching write.rs convention.

Verified: cargo check (default/server) clean; cargo check --features web
--target wasm32-unknown-unknown clean (0 errors); dx check clean; 411
cargo tests pass.
2026-06-30 13:41:23 +08:00
xfy
6e94db0c2b feat(admin): add backup/restore tab (dual-mode + task progress polling)
新增 dashmap 依赖。create_backup 探测 pg_dump 优先(含 schema,签名头前置)、
不可用回退纯 SQL(COPY TO STDOUT 逐表,按表精确进度)。restore_backup 校验
签名头(仅本系统备份)+ 路径穿越防护 + 二次确认,探测 psql 执行。
list_backups/delete_backup 管理备份,GET /api/database/backups/{name} 下载
(admin 鉴权 + 白名单)。tasks.rs DashMap 进度表(1h 惰性 GC),前端每 1.5s
轮询 get_task_progress 显示进度条/stage,完成后刷新列表。
backups/ gitignored 不直接暴露。AGENTS.md 同步新增 CodeMirror 子项目与
数据库管理章节。
2026-06-29 19:16:11 +08:00
xfy
5c4b2a53d1 feat(admin): add data export tab (Axum streaming, SQL/CSV)
新增 GET /api/database/export Axum 流式路由(镜像 upload.rs 的 cookie→admin
鉴权),支持按表/按查询导出。CSV 用 COPY ... TO STDOUT WITH CSV 流式
(大表不 OOM),SQL 逐行拼 INSERT(含类型化转义)。表名白名单 + 查询
AST 只读强制。前端 ExportTab 触发 window.open 下载。
2026-06-29 19:01:16 +08:00
xfy
a49f47c8a6 feat(admin): add SQL console tab (read-write + 4 guards + sqlparser)
新增 sqlparser 依赖(optional+server),AppError 加 BadRequest 变体(护栏
返回动态用户可见消息)。execute_sql server function 全读写执行,4 道护栏:
(1) sqlparser AST 高危语句闸门——DROP DATABASE/SCHEMA 字符串预检绝禁、
DROP/TRUNCATE/ALTER 需勾选「我了解后果」;(2) 无 WHERE 的 UPDATE/DELETE 拒绝;
(3) 复用 STATEMENT_TIMEOUT_SECS 超时上限;(4) 前端写操作二次确认。
默认禁多语句,结果 500 行截断。get_db_schema 拉表/列供 CodeMirror 补全。

前端 SqlConsoleTab:CodeMirror 编辑器(Vim + Catppuccin 主题跟随站点 +
实时 schema 补全)+ 选项 toggles + 结果表格 + EXPLAIN 输出。
2026-06-29 18:56:15 +08:00
xfy
c4c490b881 feat(admin): add server status tab (sysinfo host metrics + cache hit rate)
新增 sysinfo 依赖(optional+server),sysinfo_sampler 后台采样任务
(SYSINFO_SAMPLE_SECS 可配,默认 0.5s)周期刷新 CPU/内存/磁盘/load 到 RwLock
快照,get_server_status 只读快照零成本。cache.rs 给 9 个缓存加 AtomicU64
hit/miss 计数 + cache_stats() 聚合命中率。

前端 ServerStatusTab:应用内指标(运行时间/连接池/会话/CPU/内存/磁盘/load)+
缓存命中率表 + 刷新按钮 + 自动刷新开关(500ms/1s/2s/5s/手动,默认手动)。
2026-06-29 18:45:02 +08:00
xfy
7553dcf405 feat(admin): add database status tab (tables/connections/migration version)
get_db_status server function 查 pg_catalog 聚合:数据库总大小/连接数/
表清单(行数估算+大小+死元组)/索引占用 Top10/活跃连接(过滤自身)/迁移版本。
前端 DbStatusTab:概览卡片 + 表/索引/连接表格 + 刷新按钮 + 自动刷新开关
(1s/2s/5s/30s/手动,默认手动;wasm 端用 web_sys setTimeout 轮询,无新依赖)。
2026-06-29 18:36:08 +08:00
xfy
81a4196e43 feat(admin): add /admin/system route shell with 5 tabs
新增系统管理入口页(数据库状态/服务器状态/SQL 控制台/导出/备份恢复),
顶部 tab 切换(用 use_signal 不深链)。路由注册 + 导航加「系统」项。
tab 内容待后续 task 填充。
2026-06-29 18:26:24 +08:00
xfy
431e6e1db4 fix(lint): resolve clippy warnings in comment_storage tests and trash.rs
- comment_storage.rs: group digits consistently (3600_000 → 3_600_000)
- trash.rs: remove unused `use super::*` in test module
2026-06-29 15:54:49 +08:00
xfy
48a328f553 feat(admin): add decorative image to tiptap editor with focus-within fade effect 2026-06-29 15:45:24 +08:00
xfy
fbd706b198 style: format files and update about page content
- Update about page content

- Format code in admin pages, tags, and theme modules
2026-06-29 14:31:04 +08:00
xfy
ae770cae3a feat(ui): use EmptyState component for search no results 2026-06-29 14:14:03 +08:00
xfy
23f0cc6222 fix(ui): adjust search placeholder size and position 2026-06-29 14:10:21 +08:00
xfy
b366c00e74 fix(ui): enlarge and better center search placeholder image 2026-06-29 14:07:35 +08:00