721 Commits

Author SHA1 Message Date
xfy
ef8af77a08 style(admin): format trash.rs 2026-06-29 13:42:29 +08:00
xfy
fdaefae027 test: fix test_auto_purge_settings_has_transition_class false positive 2026-06-29 13:37:59 +08:00
xfy
768dbc9510 feat(admin): add smooth slide-down animation to trash auto-purge settings panel 2026-06-29 13:34:43 +08:00
xfy
a47a5a6ac9 feat(admin): update empty state for trash page 2026-06-29 13:26:45 +08:00
xfy
c69a632e75 fix(ui): trigger FilterTabs indicator animation on click 2026-06-29 13:24:52 +08:00
xfy
3daa09efc2 fix(ui): fix WASM compilation error in FilterTabs indicator animation 2026-06-29 13:22:22 +08:00
xfy
ec5f933191 feat(ui): add smooth sliding animation to FilterTabs active indicator 2026-06-29 13:20:39 +08:00
xfy
d91a56f8b2 refactor(admin): extract comments filter tabs into FilterTabs component 2026-06-29 13:15:50 +08:00
xfy
79af7d345e fix(admin): 修复重建内容后标题行纵向错位
点击「重建内容 / 重建全部」后,结果消息撑高了 RebuildCacheBar,外层
header 的 items-center 据此重新居中,把「文章管理」标题与「+ 写文章」按钮
整体下移,而重建按钮停在容器顶部,造成三者不再同处一行。

根因:c024de1 抽取 RebuildCacheBar 时把结果消息纳入子组件的 flex-col 文档流
(重构前消息是页面级兄弟节点,不参与 header 居中计算)。

修复:根容器改 relative,结果消息用 absolute 悬挂于按钮行下方、脱离文档流,
组件盒高恒等于按钮行高度,无论消息是否出现都不影响 header 的垂直居中。

验证:dx check + cargo clippy 全绿。
2026-06-29 11:49:28 +08:00
xfy
a1aef5bda9 fix(admin): add cursor-pointer to comment status filter buttons 2026-06-29 11:41:52 +08:00
xfy
dbe95f1173 feat(admin): use global EmptyState component in comments management page 2026-06-29 11:39:32 +08:00
xfy
72d21c6670 feat(admin): use global EmptyState component in posts management page 2026-06-29 11:38:50 +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
b923851284 docs(main): 注明 CompressionLayer 默认跳过 image/*
Some checks failed
CI / check (push) Failing after 5m37s
CI / build (push) Has been skipped
避免后续误以为压缩层会对 WebP/PNG/JPEG 等已是压缩格式的图片再次
压缩而浪费 CPU。tower-http 的 DefaultPredicate 开箱即跳过 image/*
(例外 image/svg+xml)、gRPC、SSE 及 <32 字节响应,无需额外配置。

纯文档性注释,无行为变更。
2026-06-29 11:16:30 +08:00
xfy
74f8c212f6 feat(csrf): warn at startup when APP_BASE_URL is unset
APP_BASE_URL 未设置时 trusted_origin 回退到请求 Host 头推导本站 origin,
反向代理后若 Host 头可被客户端影响存在 CSRF 绕过风险。.env.example 虽然
警告了,但默认值就是空——'cp .env.example .env 后忘改' 是最常见的部署失误。

加启动时一次性 WARN(与 image.rs 的启动告警同范式):
- warn_if_app_base_url_unset() 在 main.rs 启动序列调用,与 validate_database_url
  等配置告警归在一处
- 抽出纯函数 app_base_url_is_set() 承载判断逻辑,便于测试,打日志副作用与之解耦
- 5 个单测覆盖 unset/empty/whitespace/set/trim 边界

每请求路径的 trusted_origin 保持纯函数不变,不引入 dedup 状态或刷屏风险。
2026-06-29 11:02:58 +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
178870427c feat(ui): refactor EmptyState to accept dynamic string props and add empty state to archives page 2026-06-29 10:11:00 +08:00
xfy
82a8c511fc fix(theme): remove dark class from AppRouter div 2026-06-26 18:24:00 +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
1e78c7f0f9 fix(doc): 修复 /doc 路由与 Dioxus 静态托管冲突的 panic
Dioxus 在 dev 用 nest_service("/doc", ServeDir) 托管 public/doc/ 目录,
手动在 static_routes 注册 /doc 会在 merge 时触发路由冲突 panic。

根因(dioxus-server serve_dir_cached):dev 模式对 public/ 下的目录用
nest_service 挂整个 ServeDir,注册了 /doc;我在 static_routes 加的
精确 /doc 路由与之同名冲突。

修复:
- main.rs: 移除手动注册的 /doc 与 /doc/ 重定向路由,交给 Dioxus 托管。
  文档深层路径 /doc/yggdrasil/* 由 ServeDir 直接服务。
- Makefile: doc 目标拷贝后额外生成 public/doc/index.html 重定向页,
  ServeDir 访问目录根时返回它,经 meta refresh + JS 跳转到
  yggdrasil/index.html,让裸路径 /doc 也能直达文档。
- build/clean 同步集成 make doc 与清理 public/doc。
2026-06-26 14:27:21 +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
ac1f92d816 refactor(core): 迁移 post-content 到 yggdrasil-core,删除 public/js
逐行 TypeScript 化(逻辑不变),改走 window.__initPostContent 全局入口。
post-content.js 已迁移进 libs/yggdrasil-core/,public/js/ 目录随之删除。
2026-06-26 13:57:48 +08:00
xfy
25e01a109b fix(write): 移除 cover_uploading 多余的 mut 绑定
本组件只读取(on_submit 拦截)与传递给 CoverUploader,写入全在子组件
内部。Signal 是 Copy 类型,.set() 不要求 mut 绑定,编译器的 unused_mut
警告是真阳性。
2026-06-26 11:07:31 +08:00
xfy
33db6dc5aa feat(doc): 文档托管到 /doc 路径
- main.rs: static_routes 加 /doc 与 /doc/ 的 301 重定向到
  /doc/yggdrasil/index.html,绕开 Router 的 /:..segments 兜底。
  文档深层路径由 Dioxus 自动托管 public/ 提供,无需额外代码。
- .gitignore: 忽略 public/doc 构建产物(配合 make doc 拷贝)
2026-06-26 11:04:29 +08:00
xfy
c024de1b09 refactor(posts): 抽取 RebuildCacheBar 子组件,降低 PostsPage 复杂度
PostsPage 主体从约 181 行降至约 121 行。「重建内容 / 重建全部」工具条(两个按钮
+ 状态信号 rebuilding/rebuild_result + do_rebuild 异步闭包 + 结果消息)整体迁入
RebuildCacheBar 子组件,完全自洽,无任何 prop。

布局调整:原结果消息是页面级兄弟节点,现纳入子组件垂直容器(按钮行在上、消息
在下),保持「消息显示在按钮下方」的视觉语义;+ 写文章 链接仍与工具条在标题行
水平并排。

依据 dioxus-render-purity skill 的 god component 治理建议。验证:cargo build +
dx check + cargo test (406) 全绿。
2026-06-26 10:50:29 +08:00
xfy
a534ee436f refactor(trash): 抽取 AutoPurgeSettings 子组件,降低 TrashPage 复杂度
TrashPage 主体从约 484 行降至约 266 行。自动清理配置(可折叠设置面板:
开关 + 保留天数步进 + 保存 + 已保存反馈)整体迁入 AutoPurgeSettings 子组件,
含 6 个私有 signal(draft_days/enabled、panel_open、saving、just_saved)+ dirty
memo + chevron_rotate 派生。

接口:settings(已保存配置)作为双向绑定 signal prop——子组件加载/保存时写入,
父组件读取 retention_days 供 TrashRow 的「剩余天数」展示。比 CoverUploader 的
两 signal 接口更简。

附带简化:原 settings_loaded 守卫删除——子组件挂载即触发一次自身 effect 加载
配置,不再嵌入列表翻页 effect,逻辑更清晰。

依据 dioxus-render-purity skill 的 god component 治理建议。验证:cargo build +
dx check + cargo test (406) 全绿。
2026-06-26 10:31:23 +08:00
xfy
aa3bbdc522 docs(pages): 补充 pages 模块文档
最后一个缺模块文档的文件,至此 cargo doc 三维度全满:
371 pub 项全覆盖 / 122 文件模块文档全覆盖 / 零 rustdoc 警告
2026-06-26 10:05:40 +08:00
xfy
f342742820 docs(write): 清理 CoverUploader 重复/失实的文档注释
最终 review 指出 c2eb261 留下了一段重复且失实的 doc(仍声称 cover_uploading
对本组件私有,与已改为双向绑定 prop 的事实矛盾)。删除旧段落,保留正确描述
两个双向绑定 signal 的版本。
2026-06-26 09:57:15 +08:00
xfy
c2eb2614bb fix(write): 恢复 cover_uploading 上传中保存拦截
code review 指出上一提交(835ada0)移除 on_submit 的 cover_uploading()
拦截是真实回归:uploads_in_flight 只覆盖编辑器内图片上传,不含封面上传
路径(upload_image_file 是独立 async,不更新该计数)。用户选大图后立即
点保存会把空/旧封面落库,封面被静默丢弃。

修复:cover_uploading 改由父组件 write_editor 声明并作为 prop 传入
CoverUploader(与 cover_image 同为双向绑定 signal),恢复 on_submit 的
上传中拦截。子组件仍负责 set,父组件读,接口从 1 个 signal 扩为 2 个
(cover_image + cover_uploading),均为父组件真正需要的状态。
2026-06-26 09:52:55 +08:00
xfy
835ada094d refactor(write): 抽取 CoverUploader 子组件,降低 god component 复杂度
write_editor 从 746 行降至约 480 行。封面上传(5 个私有 signal + 上传闭包 +
封面 rsx)整体迁入 CoverUploader 子组件,仅通过 cover_image signal 双向绑定
与父组件通信。依据 dioxus-render-purity skill 的 god component 治理建议。

附带调整:
- META_LABEL_CLASS / META_INPUT_CLASS 提升到模块级常量,供 write_editor 与
  CoverUploader 共用(原为 write_editor 函数局部 const,CoverUploader 取不到)。
- 移除 on_submit 中的 cover_uploading() 上传中拦截:该 signal 已私有化到子组件,
  且上传耗时极短(前端并发上传有 uploads_in_flight 拦截兜底),收益不抵跨组件
  暴露 signal 的复杂度。

验证:cargo build + dx check + cargo test (406) 全绿。
2026-06-26 09:42:14 +08:00
xfy
afebd441ae refactor(trash): dirty 派生值改用 use_memo
依据 dioxus-render-purity skill 规则二:派生值不应在 render 期内联重算。
dirty 是 settings_draft_* 与 settings 的纯派生布尔值,改用 use_memo,
依赖信号不变时跳过 trim/parse 重算。同步更新两处使用点为 dirty() 调用。
2026-06-26 09:33:13 +08:00
xfy
96da2f27d5 docs: 补齐 tiptap_bridge 文档注释并新增 make doc 目标
- tiptap_bridge.rs: 为 wasm-bindgen 桥接层全部 19 处 pub 项补文档
  (JS 类型映射、getter/setter、EditorHandle::new、pub use 重导出)
- markdown.rs: 用反引号包裹注释内字面量 <span>,消除 rustdoc 警告
- Makefile: 新增 doc / doc-open,纯 binary crate 需 --document-private-items
  才能让内部模块进文档
2026-06-25 18:26:56 +08:00
xfy
225bb2406f docs(skill)+fix: 新增 dioxus-render-purity skill 并修复 post_detail 渲染期 set signal
依据 Dioxus 0.7 antipatterns 官方指南,做两件事:

1. 新增 skill .agents/skills/dioxus-render-purity/
   把「render 函数必须纯净」做成可复用 skill,列出三类反模式(render 期
   副作用 / 用 use_signal 存派生值 / 用 use_effect 算派生值)及自检清单,
   防止后续编辑(人或 AI)再次在组件里写 signal.set 等副作用。触发关键词
   覆盖 dioxus/component/rsx/signal/use_effect 及对 src/pages、src/components
   的编辑。

2. 修复 post_detail.rs 的真实违规(被该 skill 捕获的典型反例):
   原代码用 use_signal 镜像 slug prop,再在 render 期 if slug_signal()!=slug
   { slug_signal.set(...) } 触发 server future 重跑——这是文档明确点名的
   「don't mutate state during render」。改为直接读 prop:
   use_server_future(move || get_post_by_slug(slug.clone())),路由 slug 变化
   时 Dioxus 自动重跑 future,无需镜像信号。key 改用 post.slug(post 已在
   match 分支内可用,避免 slug 被 move 后再读)。同步更新模块文档注释。

验证:dx check 通过、cargo test 405 passed。
2026-06-25 18:24:57 +08:00
xfy
9a00972be4 fix: 修复格式化工具破坏的单行 if/else 与闭包语法
格式化工具在展开单行语句时引入了 5 处语法错误,导致 dx serve 无法编译:

- comments.rs / trash.rs:on_select 的 `if checked { insert } else { remove }`
  丢失 else 分支,变为悬空语句 + 括号不匹配。
- trash.rs(3 处):`web_sys::window().and_then(|w| {...}).unwrap_or(false) {`
  展开闭包体时漏掉闭合 `)`,吞掉了 if 块的开括号。
- write.rs:封面图 `if cv.contains('?') {...} else {...}` 丢失 else,
  多出一个 `}`,导致 src 块括号失衡。

均还原为正确的多行 if/else 结构。验证:dx check 通过、cargo test 405 passed。
2026-06-25 18:11:35 +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
c836e3e1df feat(api): 新增 healthz/readyz 健康检查端点
Some checks failed
CI / check (push) Failing after 7m29s
CI / build (push) Has been skipped
新增两个无中间件探针端点,供 Docker HEALTHCHECK 与反向代理/负载
均衡使用:

GET /healthz — liveness 存活探针,进程在跑即 200,不查 DB。
GET /readyz  — readiness 就绪探针,SELECT 1 检测 DB 连通性(2s
超时),不可达返回 503,附带连接池 size/available/max_size/waiting
指标。

路由挂载在 static_routes(无 CSRF/超时/缓存中间件),避免被
add_cache_control 误加 max-age 缓存头导致探针误判。readyz 直接用
DB_POOL.get() 不走 get_conn 的退避重试,保证探针快速失败。

dx check + clippy + 405 tests 全通过。
2026-06-25 16:45:09 +08:00
xfy
d41eb4b3cd refactor(write): 滚动条从内容区内部移到窗口边缘
将 /admin/write 从「锁一屏 + 内容区内部滚动」改为「整页自然滚动」,
滚动条出现在浏览器窗口右侧而非中间内容区边缘。

admin_layout: write 路由 root 由 h-dvh overflow-hidden 改为 min-h-dvh
自然文档流,main 去掉 overflow-hidden。

write.rs: 根 div 与内容区去掉 flex-1/min-h-0/overflow-y-auto 等滚动
约束;编辑器高度由 flex-1 改为 h-[60vh](min-h-[400px] 兜底);底部
操作栏去掉 flex-shrink-0,改为跟随页面滚动。

整页滚动时 Header 仍 sticky 吸顶(带毛玻璃),保存栏随内容滚走。
2026-06-25 16:24:26 +08:00
xfy
7611c60e6c feat(write): 保存按钮改用鼠尾草绿主色
Some checks failed
CI / check (push) Failing after 5m21s
CI / build (push) Has been skipped
write 页的「保存」主按钮原为石墨黑 bg-paper-primary,与 dashboard
「写文章」等主操作按钮颜色不一致。改为 bg-paper-accent 鼠尾草绿,
统一后台所有主操作按钮配色。hover 改用 brightness-110 + active
scale 与其它主按钮交互手感一致。禁用态(saving)保持 paper-tertiary 灰。
2026-06-25 15:14:31 +08:00
xfy
806b474599 feat(theme): 新增冷调玫瑰第二色用于次要按钮
在 input.css 的 @theme 新增 accent-2 / accent-2-soft 主题变量,
用于与鼠尾草绿主色成对的次要操作按钮。

颜色选择经 HSL 分析:色相 342°(冷调玫瑰,区别于暖砖红 9°),
饱和度 28%(低饱和从属于主色),保证视觉为浅色系且不浊。
对比度:亮色 #83495b vs 米白底 6.5:1、暗色 #cca4b0 vs entry 底 7.46:1,
均过 WCAG AA。

新增 BTN_SECONDARY 常量(ghost 描边风格)用于 dashboard「管理文章」
按钮,与「写文章」实心主按钮构成主次层级。两按钮统一用
inline-flex items-center justify-center 保证文字水平垂直居中、等高。
2026-06-25 15:06:45 +08:00
xfy
519c0ae3a9 feat(admin): 后台迁移至 paper-* 主题变量对齐前台
将 /admin 路由下硬编码的 Tailwind gray + hex 配色统一迁移到
input.css 定义的 paper-* 主题变量系统,使后台视觉完全对齐前台:

- 底色:bg-white → bg-paper-theme(米白,与前台一致)
- 卡片/表格:bg-white dark:bg-[#2e2e33] → bg-paper-entry
- 主操作按钮(写文章/保存/分页):bg-gray-900 黑 → bg-paper-accent 鼠尾草绿
- 文字/边框/hover:gray-* + dark:hex → text-paper-*/border-paper-*
- 清理 trash 页散落的 #5c7a5e/#7da97f/#e8f0e8 等 hex,路由到变量
- 骨架占位统一复用 ADMIN_* 常量与 SkeletonBox

保留 green/amber/red 状态语义色(通过/待审/删除)与 toggle 白色滑块。
统一采用 bg-paper-* 工具类语法,与 header/post_card 主流写法一致。

dx check + clippy + 402 tests 全通过。
2026-06-25 14:33:48 +08:00
xfy
df85df3d2e style(empty-state): 配图加圆角,暗色模式降低亮度
- rounded-lg 圆角(= 项目的 --radius-paper 8px)
- dark:brightness-90 滤镜,让彩色配图在暗色主题下退后一些
2026-06-25 13:48:46 +08:00
xfy
cfd2af1fab refactor(empty-state): 内联 SVG 换成 webp 配图
移除手绘的 LineDog 内联 SVG,改用 <img> 引用
public/images/xiaotiaoxiaogou_01.webp 原始插画。
2026-06-25 13:43:23 +08:00
xfy
8b581649a6 feat(home): 用「线条小狗」插画替换首页空状态
原空状态仅有一行「暂无文章」文案。新增 EmptyState 组件:
- 内联单色 SVG 配图(双手持相机,取景器内两只小狗),由 webp 重绘为线条画
- 描边用 currentColor 适配明暗主题,屏幕区填 accent-soft 作焦点
- 标题改「还没有文章」,副文案呼应站点定位
- 保留可选 action 字段供后台上下文复用

颜色、圆角、入场动画均沿用项目 Forest 调色板与既有视觉语言。
2026-06-25 13:37:07 +08:00
xfy
4ec8d72b66 feat(db): 启动期自动创建目标数据库
把 scripts/migrate.sh 里 CREATE DATABASE 的逻辑内置进二进制启动流程,
全新部署不再需要手动建库即可首次启动。

- src/db/pool.rs: 新增 ensure_database(),连接 postgres 维护库做
  CREATE DATABASE IF NOT EXISTS 等价逻辑(SELECT EXISTS + CREATE)。
  复用 MIGRATE_STARTUP_TIMEOUT_SECS 窗口应对 DB 起得慢;目标库名经
  is_simple_ident 校验后才拼到 SQL,不合法的名字跳过让正常路径报错。
  tokio_postgres::Config 不 Clone,故逐字段拷贝到新 Config。
- src/main.rs: 在 get_conn_for_startup() 之前调用 ensure_database(),
  失败走统一的 tracing::error! + exit(1) 路径。
- src/db/migrate.rs: 文档注释补明目标库存在性由 ensure_database 上游保证。

新增 2 个 is_simple_ident 单测(392 → 402 tests)。
2026-06-25 11:46:50 +08:00
xfy
21322a4339 fix(card): 提升覆盖层 z-index 使封面图可点击跳转
封面完整图 .blur-img-full 在全局 CSS 有 z-index:1 (input.css:611),
盖在原 z-0 的覆盖层 Link 之上,导致 hover 封面命中 <img> 而非 <a>:
光标非 pointer 且点击不跳转。

将覆盖层提到 z-[2](>封面图 1,<标签 10),命中 <a> 后光标与点击一并解决,
标签 stop_propagation 与 hover 缩放不受影响。
2026-06-25 11:10:43 +08:00
xfy
71014aa0de style: 封面图比例统一改为 21:9 超宽
卡片 16:9 仍偏高,统一改为 21:9(超宽荧幕比例),前后台一致:
- input.css: .blur-img.post-card-cover-blur aspect-ratio 16/9 → 21/9
- post_card.rs: 缩略图 thumb=400x225 → 420x180 匹配 21:9
- write.rs: 编辑器封面区 aspect-video → aspect-[21/9]
2026-06-24 15:53:55 +08:00
xfy
5fc8a9aeeb style(card): 卡片封面图比例从 4:3 改为 16:9
- input.css: .blur-img.post-card-cover-blur 的 aspect-ratio 4/3 → 16/9
- post_card.rs: 缩略图请求 thumb=400x300 → 400x225 匹配 16:9
2026-06-24 15:49:53 +08:00
xfy
66d29eaa62 fix(write): 元数据行移至封面上方 + 编辑器撑满修复
布局调整:
- 元数据行(Slug/标签)从封面图下方移到上方,顺序变为
  标题 → 摘要 → 元数据行 → 封面图 → 编辑器

- 滚动主体容器补 flex flex-col:此前 overflow-y-auto 容器内
  子项的 flex-1 失效,导致编辑器只剩 min-h 不撑满。
  加 flex flex-col 后,flex-1 编辑器在窗口够高时正常填充剩余空间。
2026-06-24 15:35:52 +08:00
xfy
198a9b292a refactor(write): 封面区空态矮横条 + 主体可滚动
优化编辑器可用高度,避免封面区过度挤压:

A. 封面区空态改为矮横条(h-14,图标+提示+URL链横向排列),
   不再占用 16:9 的大块空间;有图/上传中时才展开 aspect-video。

B. 元信息区+编辑器+错误提示包裹进 overflow-y-auto 滚动容器,
   底部操作栏固定吸底(border-t 分隔)。
   编辑器区从纯 flex-1 改为 min-h-[400px],保证最小编辑空间,
   窗口过矮时不被元信息挤压到不可用。
2026-06-24 15:23:32 +08:00