33 Commits

Author SHA1 Message Date
xfy
65a7b1226f style: 全项目格式规范化(cargo fmt)+ Docker daemon 断连容错
- 全项目 .rs 统一 cargo fmt 排版(换行/缩进/参数分行/导入排序)
- infra/docker: DOCKER_CLIENT 连接失败不 panic,降级返回 None
  (博客不依赖 Docker,panic=abort 下会导致整个进程崩溃)
- infra/mod: 去除多余空行
- database/mod: 模块声明按字母序重新排序
2026-07-15 17:22:06 +08:00
xfy
cb77d28a68 fix(theme): 跟随系统切换改为瞬切,放弃 VT 动画
诊断确认:跟随系统与手动点击的 VT 流程在 JS/CSS 层完全等价(animationName、
--tt-r、bg 翻转、clip-path 全部正确),但视觉上跟随系统场景下圆形展开动画
不显示,仅瞬切。系统偏好变化是后台事件,VT 在此上下文下动画不可靠。

改用设置语义的瞬切:JS 侧新增 applyResolvedTheme(isDark) 入口(非翻转语义,
直接 add/remove dark class),Rust effect 改调 __applyResolvedTheme 传实际
明暗值,移除圆心计算逻辑。职责清晰:
- 手动点击 → __startThemeTransition → VT 圆形展开(用户手势,动画可靠)
- 跟随系统 → __applyResolvedTheme → 瞬切(后台同步语义)
2026-07-03 13:42:17 +08:00
xfy
eb3087edf4 fix(theme): 跟随系统切换用主题按钮位置作为 VT 圆心
诊断发现跟随系统与手动点击的 JS 路径完全一致(__startViewTransition 全程
正常,无降级、无 reject),「看起来像淡入淡出」实为圆形展开动画在屏幕中心
圆心下的必然视觉效果:从中心向四周等距扩散,各方向同步推进,近似整体覆盖,
与手动点击(按钮在右上角,圆弧明显横扫)观感差异大。

改用 .theme-toggle 按钮的 getBoundingClientRect() 中心作为 VT 圆心,与手动
点击的动画起点一致;按钮不在 DOM 时回退到视口中心。同步更新注释说明。
2026-07-03 13:26:40 +08:00
xfy
188fc464e2 fix(theme): System 模式下系统偏好变化时同步 dark class
原实现存在缺口:<html> 的 dark class 运行时写入点只有「首屏预加载脚本」与
「手动点击 ThemeToggle」两处,System 模式下系统颜色偏好自动变化时无人同步——
尽管 system_dark signal 与 resolved memo 都会更新,下游(CodeMirror 等)也
能跟随,但页面配色纹丝不动,与文件头注释承诺的「实时同步 .dark class」不符。

新增 use_effect 兜底:仅 System 模式下 resolved 实际翻转时,以视口中心为圆心
复用 __startThemeTransition 触发 VT 圆形展开动画。Light/Dark 显式模式由
ThemeToggle::onclick 全权负责,effect 不介入,避免重复触发;首次挂载通过
prev_resolved 信号记录基线,自然跳过(DOM 已由 ThemePreload 设好)。
2026-07-03 11:35:10 +08:00
xfy
fee5b7ba94 refactor(wasm): 消除 DOM 互操作中的 js_sys::eval 字符串求值
对照 Dioxus "Breaking Out" 文档审查后,将 3 处 js_sys::eval 改为
类型化调用,与 tiptap_bridge/codemirror_bridge 风格统一:

- utils/time.rs sleep_ms:eval("new Promise(r=>setTimeout(r,n))")
  改为 js_sys::Promise + web_sys::Window::set_timeout_*,复用项目内
  system.rs::wasm_sleep 同款写法。
- theme.rs 主题切换:format!-into-eval 拼 __startThemeTransition(x,y)
  改为 Reflect::get 取函数 + Function::call2,消除字符串注入面
  (原坐标虽为 f64 无实际风险,但属不必要 eval)。
- post_content.rs:__initPostContent / __initLightbox 两处 eval 字面量
  改为 invoke_optional_global 辅助函数(Reflect::get + Function::apply),
  消除旧版 "eval 字符串粘贴" 模式;__lightboxSelectors 直接复用 JsValue
  传入,省去一次回读。
2026-07-02 17:15:18 +08:00
xfy
ba5b146dfa fix(theme): 消除 detect_initial_theme 的 unreachable_code 警告
wasm 分支末尾的 return Theme::System 在 wasm target 下让尾部兜底
Theme::System 不可达(unreachable_code 警告)。把尾部兜底用
#[cfg(not(target_arch = "wasm32"))] 限定,使其只在 server 分支
(cookie 未命中)或边角构建时编译,wasm target 下函数以 wasm
分支的 return 自然结束,两端都零警告。
2026-07-02 16:49:31 +08:00
xfy
f00c2bf963 refactor(hooks): 新增 use_event_listener 通用事件监听 hook
把 theme.rs 与 footer.rs 两处手写的 "use_hook 持有 Closure+target、
use_effect 注册 add_event_listener、use_drop 移除" 样板收口到一个
通用 hook,按 Dioxus 0.7 custom hooks 范式封装。

hook 设计:
- target 通过 acquire 闭包在 use_effect 首次运行时获取(此时 DOM 一定
  可用),调用方无需再自己包 use_effect。
- 泛型 AsRef<EventTarget> 统一 Window / Element / MediaQueryList。
- 非 wasm32 整体编译为 noop,调用方用 #[cfg] 隔离 web_sys 调用即可。

footer 的 "注册后首次同步" 业务逻辑保留在 hook 之外(语义不属于
事件监听)。theme 的 handler 改为重新 match_media 读取 matches,
功能等价(低频事件,开销可忽略)。
2026-07-02 16:02:51 +08:00
xfy
a10003c90d feat(theme): 切换按钮点击立即换图标并播放进入动画
将 theme.set 从 450ms 延迟改为立即执行——图标在点击瞬间切换,不再
等待圆形 VT 动画结束。原作者担心重渲染打断 VT 实属多余:VT 伪元素
快照在 __startThemeTransition 内已同步拍好,后续真实 DOM 变化不影响。

- 移除 click_gen 防抖机制与 spawn_local 延迟回调(不再需要)。
- SVG 加 key 属性,theme 变化时 Dioxus 重新挂载 SVG,触发 CSS 进入动画。
- input.css 新增 @keyframes theme-icon-enter(缩放 0.5→1 + 旋转 -60°→0°
  + 淡入,0.3s ease-out),仅在 .theme-toggle svg 上生效。
- prefers-reduced-motion 下禁用该动画,遵循项目既有约定。
2026-07-02 12:00:00 +08:00
xfy
9a4e6c3e6e docs(agents): 校正构建/测试/迁移说明,对齐当前代码库
- 移除不存在的 ./migrate.sh:迁移实际由 src/db/migrate.rs 在启动期自动运行,
  目标库由 db/pool.rs::ensure_database_exists 自举创建;补充新增迁移需在
  MIGRATIONS 数组登记(编译期校验磁盘与数组一致)
- npm → pnpm:4 个 libs 均使用 pnpm-lock.yaml,Makefile 全程 pnpm
- make test / make build 覆盖 4 个 libs(补充 codemirror-editor)
- 补充 restore-webp 兜底步骤的成因(dx 0.7.9 重编码 .webp)
- 补全 make clippy(-D warnings)、make fix、make clean 真实行为
- 模块树补齐 context/router/ssr_cache/sysinfo_sampler 及 *_bridge
- 4 个前端子项目合并为对比表,保留 wasm-bindgen 接线等关键细节
- 保留 Workflow 提交规范
2026-07-02 11:40:47 +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
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
46e9a29f1e fix(theme): 消除 ThemeToggle 在 wasm/server 构建的 unused_mut 警告
Some checks failed
CI / check (push) Failing after 6m25s
CI / build (push) Has been skipped
dx build 的 wasm target 报两条 unused_mut:

- theme (160行): wasm 端的 theme.set 被非 wasm cfg 分支剥离,写操作转移
  到副本 theme_clone,theme 本身在 wasm 下只被读取/Copy。加
  #[cfg_attr(target_arch = "wasm32", allow(unused_mut))]——仅 wasm 端压住,
  server 端仍需 mut(theme.set(next) 实际存在)。
- gen_clone (191行): 闭包内只读 gen_clone(),从未 set,两端都不需 mut,
  直接删除。

顺带把原来无条件挂在 click_gen 上的 #[allow(unused_mut)] 改成
#[cfg_attr(not(target_arch = "wasm32"), ...)]——click_gen 只在 wasm 分支被
set(189行),server 端该分支被剥离。改为按 cfg 方向标注,与 theme 对称,
语义更精确(不再无差别压住 wasm 端的检查)。

匹配项目既定模式(post_cover.rs:20 的 cfg_attr allow)。零行为变更,
dx check / cargo check(双 target) / clippy / theme 测试均通过。
2026-06-29 11:31:57 +08:00
xfy
cb73a9b979 chore: fix cargo clippy warnings (unused import and false positive unused_mut) 2026-06-29 10:14:27 +08:00
xfy
ba9db2d75a fix(theme): fix dark→light transition animation invisible
The static CSS rule 'animation: none' on ::view-transition-new(root)
caused the new (light) screenshot to be fully visible before the JS-
injected clip-path @keyframes kicked in. This meant the light layer
was already covering the dark layer, making the expanding circle
invisible.

Fix: move all VT pseudo-element styles (animation, mix-blend-mode)
into the dynamically injected <style> block in vt.ready, so they
apply atomically with the clip-path keyframes.

Also:
- Add transition:none !important during VT to prevent body's
  background-color 0.3s transition from producing a stale capture.
- Restore Dioxus theme.set() that was disabled for debugging, so
  the toggle icon and localStorage persist correctly.
- Use !important on injected animation to reliably override any
  UA default VT animations.
2026-06-26 17:50:45 +08:00
xfy
30a861f910 docs(agents): 新增 yggdrasil-core 子工程章节
与 lightbox/tiptap-editor 并列,记录用途、构建、注入方式与
主题展开动画的 View Transitions 实现。同步更新 make test 注释
与 Build Artifacts 列表。
2026-06-26 14:09:46 +08:00
xfy
6979bd1010 feat(theme): 圆形展开主题切换动画(View Transitions API)
点击主题按钮时,新主题从按钮位置以圆形向外展开覆盖全屏。
JS 同步 toggle dark class 拍快照 + CSS clip-path circle keyframes;
Rust theme.set 事后对齐状态(use_effect 幂等)。
reduced-motion 与不支持 VT 的浏览器自动降级为瞬切。
2026-06-26 14:06:23 +08:00
xfy
0398cc6c66 perf(render): 列表加显式 key + release 启用 panic=abort
依据 Dioxus 0.7 optimizing 指南做两项改进:

- 为数据驱动列表渲染补全显式 key(13 处,9 个文件):
  home/archives/tags/search 文章与标签列表、post_card/post_footer
  标签、header 桌面/移动导航、admin dashboard/posts/write 列表。
  使用各类型稳定唯一字段(post.id / tag / tag.name / item.label /
  err.id / year_group.year / month_group.month_en)作为 key,
  让 Dioxus diff 正确识别元素身份。骨架屏 for _ in 0..N 静态占位
  循环与已带 key 的评论树/admin 行不动。

- [profile.release] 加 panic = "abort":WASM 去掉 unwind 元数据减小
  体积;server 端错误处理走 Result+?(见 error.rs),不依赖 panic
  unwind,无副作用。

其余文件为本机格式化(单行折叠/多行展开),一并提交。

验证:dx check 通过、cargo test 405 passed。
2026-06-25 18:07:22 +08:00
xfy
449a545886 security: fix critical issues from repository review
Some checks failed
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
P0 blockers:
- Fix migration numbering conflict and duplicate indexes
- Change comments.post_id FK to ON DELETE CASCADE
- Restrict public post detail endpoint to published posts only
- Fix rate-limiting IP extraction and fallback to ConnectInfo
- Harden HTML sanitizer: deny unknown URL schemes, restrict data URIs
- Remove session token from login response body
- Enforce image pixel/dimension limits on upload and serving

P1 high-risk:
- Validate uploads by magic bytes and decode GIF/WebP
- Add pagination/rate-limiting to search, tag posts, and comments
- Make first-admin registration and slug uniqueness check atomic
- HTML-escape comment author fields
- Improve HTML minify cache key and skip admin/error responses
- Add mobile navigation menu

P2 accessibility/quality:
- Associate form labels with inputs
- Key PostDetail article by slug to re-init scripts on navigation
- Improve image viewer keyboard accessibility
- Make theme toggle SSR-friendly and add aria-label
- Invalidate slug 404 cache on create and pending count on new comment
- Deduplicate tags case-insensitively

P3 cleanup:
- Remove unused tower-http dependency, expand make clean
- Configure DB pool timeouts and verified recycling
- Run background cleanup tasks immediately on startup
- Use SHA-256 for stable disk cache keys
- Log DB errors with Display instead of Debug
- Update README migration instructions

All tests pass (321), clippy clean, dx check clean.
2026-06-17 10:34:14 +08:00
xfy
cafbddb861 refactor: replace server-only dead_code allows with cfg(feature = "server")
Replace #[allow(dead_code)] on server-only helpers with #[cfg(feature = "server")]
to make the server/WASM split explicit and avoid compiling unused server logic
into the WASM frontend.

- Gate password/session/auth/comment helpers and model parsers with server feature
- Gate related imports and tests accordingly
- Remove genuinely unused CreatePostRequest and CreateCommentRequest
- Keep #[allow(dead_code)] for true dead code (stub methods, unused public APIs)
- Use #[cfg(any(target_arch = "wasm32", test))] for THEME_KEY
- Update AGENTS.md note
2026-06-16 16:45:08 +08:00
xfy
19ffcada4a test: 移除脆弱断言与凑数测试(review 修正)
对本次新增测试自审后的清理:

1. webp: encode_lower_quality_produces_smaller_or_equal_bytes
   原断言“低质量体积 <= 高质量体积”是 WebP 的统计趋势而非
   确定性不变量,依赖底层 libwebp 量化策略,纯色图上高低差异
   接近 0,未来库升级易变为 flaky。改为验证两个质量档位都能
   成功编码并产生可被本模块解码的合法 WebP。

2. theme: theme_is_copy
   Copy 语义由编译器在编译期保证,运行期断言永真,不测任何
   运行逻辑,属凑数。

3. webp: webp_config_default_quality_and_method
   构造 struct 再断言字段等于构造时填入的值,是同义反复。

净减 2 个测试(292 → 290),全部通过。
2026-06-15 11:26:24 +08:00
xfy
4e89412cde test(theme): 扩充主题模块测试覆盖
原仅 2 个 toggle 测试,补充后覆盖:
- toggle 的对合性(连续切换两次回到原值)
- Theme 的 PartialEq / Copy trait 行为
- 首屏预加载脚本的关键行为契约:读取 localStorage、
  回退 prefers-color-scheme、添加 dark class、
  包裹 try/catch 防止禁用 localStorage 时抛错

共 9 个测试,全部通过。
2026-06-15 10:57:51 +08:00
xfy
1904907add docs(hooks, theme): 补充中文注释 2026-06-12 19:07:43 +08:00
xfy
4fe26f7eb3 test: improve unit test coverage and assertions
Some checks failed
CI / check (push) Failing after 15m51s
CI / build (push) Has been skipped
- Add tests for sanitizer, mime_to_ext, clean_tags, Theme::toggle
- Tighten assertions in highlight, markdown, post status classes
- Add boundary and XSS cases for comments, slug, rate_limit, webp
- Update Cargo.lock for serial_test dev-dependency
2026-06-12 18:13:51 +08:00
xfy
f9d23d1eed refactor(ui): redesign with warm palette, sage accent, and consistent theme vars
- Warm editorial color palette (#faf9f6 light / #141416 dark)
- Sage green accent (#5c7a5e) for nav, links, buttons, tags
- Replace all hardcoded hex colors with CSS theme utilities
- Serif font (system Georgia) only on header logo
- Better hover states: scale, brightness, color transitions
- Accent-colored focus rings on inputs
- Fix language mixing: Back to Home → 返回首页
- No external font dependencies
2026-06-11 10:29:11 +08:00
xfy
0a33a843f8 fix(theme): resolve hydration mismatch in ThemeToggle by deferring icon render until mounted 2026-06-05 17:07:23 +08:00
xfy
ccffab851a fix: admin pages use client-only fetching; fix theme cookie parsing; restore custom skeletons 2026-06-03 14:55:50 +08:00
xfy
6b3e086628 feat: read theme from cookie during SSR 2026-06-03 14:38:00 +08:00
xfy
61c1ec7282 修复暗色模式:class 变体、状态同步、FOUC 预防、系统偏好
- 配置 Tailwind v4 @custom-variant dark 使用 class 模式
- 用 use_context_provider 共享主题状态,修复 AppRouter/ThemeToggle 不同步
- 默认跟随系统偏好(matchMedia),无 localStorage 时自动检测
- 统一暗色标记为 .dark class,移除 data-theme 冗余
- ThemePreload 内联脚本在 DOM 解析前设置 class,消除首屏闪烁
- SVG 内联 + currentColor,图标颜色随主题切换
- Cargo.toml 补充 web-sys MediaQueryList/DomTokenList feature

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 11:31:06 +08:00
xfy
3079f1a505 ThemeToggle:用 SVG 图标替换 emoji,移除灰色背景
- 亮色模式使用太阳 SVG 图标
- 暗色模式使用月亮 SVG 图标
- 移除按钮的灰色背景,改为 hover 透明度变化

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 11:11:44 +08:00
xfy
8b3aab21de 恢复首页路由,添加归档/标签/搜索/关于路由占位
- router.rs: / 恢复为 HomePage,新增 /archives /tags /search /about
- mod.rs: 导出 home 模块
- theme.rs: ThemeToggle 从浮动按钮改为内联样式
- home.rs: 创建空占位文件

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:31:52 +08:00
xfy
5afd8b597c Fix clippy warnings
- Remove unnecessary clone on Copy type (navigator)
- Allow dead_code for THEME_KEY constant

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:51:12 +08:00
xfy
14b92c3a89 US-005: 后台页面与路由整合
- src/router.rs: Dioxus 路由定义 (/login, /register, /admin, /)
- src/main.rs: 整合所有模块 + server block 启动 dotenvy + session 清理任务
- src/tasks/session_cleanup.rs: 每小时清理过期 session
- 修复编译错误: chrono serde, tokio-postgres chrono, argon2 OsRng
- 移除 server_context 依赖,简化 get_current_user 查询
- Tailwind CSS CDN + darkMode class 配置

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:29:50 +08:00
xfy
4a77f2c457 US-004: 前端页面 - 注册与登录
- src/pages/register.rs: 注册表单 + 前端验证 + 错误提示
- src/pages/login.rs: 登录表单 + cookie 设置 + 跳转
- src/pages/admin.rs: 认证检查 + 欢迎信息 + 登出
- src/theme.rs: 暗色/亮色主题切换 + localStorage 持久化
- Tailwind CSS CDN + dark: modifier 实现主题

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:24:05 +08:00